4、List源码解析

87 阅读6分钟

1、ArrayList源码

arraylist底层实际上是一个数组结构transient Object[] elementData;,所以arraylist集合拥有数组的特性,可以方便的通过索引进行访问。

  • 1、初始化构造函数
//默认大小的数组,空的
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//先查看空参的构造函数,在创建的时候会赋值为一个空的数组
public ArrayList() {  
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  
}
//有参构造
public ArrayList(int initialCapacity) { 
	//先查看初始化大小是否大于零
    if (initialCapacity > 0) {  
	    //创建指定大小长度的数组
        this.elementData = new Object[initialCapacity];  
    } else if (initialCapacity == 0) {  
	    //空置的时候,创建空的数组
        this.elementData = EMPTY_ELEMENTDATA;  
    } else {  
        throw new IllegalArgumentException("Illegal Capacity: "+  
                                           initialCapacity);  
    }  
}
  • 2、add、addAll
//默认初始化大小10
private static final int DEFAULT_CAPACITY = 10;
//添加方法
public boolean add(E e) {  
	//1、判断内部容量
    ensureCapacityInternal(size + 1);  // Increments modCount!!  
    elementData[size++] = e;  
    return true;  
}
//1、容量判断是否扩容,传入当前size+1
private void ensureCapacityInternal(int minCapacity) {  
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  
}
//1.1、计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {  
	//如果是空数组,获取DEFAULT_CAPACITY和minCapacity的最大值
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  
        return Math.max(DEFAULT_CAPACITY, minCapacity);  
    }  
    //否则返回size + 1大小
    return minCapacity;  
}
//1.2、是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {  
    modCount++;  //修改的次数,迭代器使用
  
    // size+1  > 数组的长度的时候
    if (minCapacity - elementData.length > 0)  
	    //增长
        grow(minCapacity);  
}
//1.3、扩容函数
private void grow(int minCapacity) {  
    // overflow-conscious code  
    int oldCapacity = elementData.length;  
    //新数组长度 = 元素数组长度的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);  
    //如果还不够,则新数组长度 = minCapacity
    if (newCapacity - minCapacity < 0)  
        newCapacity = minCapacity;  
	//新数组长度大于
    if (newCapacity - MAX_ARRAY_SIZE > 0)  
        newCapacity = hugeCapacity(minCapacity);  
    // minCapacity is usually close to size, so this is a win:  
    //扩容后数组拷贝
    elementData = Arrays.copyOf(elementData, newCapacity);  
}

1、add方法调用的时候,会进行扩容。构造函数创建的时候,会创建空的数组,在进行添加的时候,才会创建指定大小的数组 2、默认数组大小为10,这是出于性能损失与空间损失之间的最佳匹配考量。而扩容因子是1.5:因为扩容不太太小,太小容易频繁进行扩容,从而进行数组的频繁复制;而也不能太大,当扩容因子大于2的时候,新的容量刚好大于之前废弃的容量,容易造成空间的浪费。而且1.5可以充分的利用移位的操作

  • add方法,添加到指定位置
public void add(int index, E element) {  
	//范围检查
    rangeCheckForAdd(index);  
	//扩容检查
    ensureCapacityInternal(size + 1);  // Increments modCount!!  
    //数组移动
    System.arraycopy(elementData, index, elementData, index + 1,  
                     size - index);  
    elementData[index] = element;  
    size++;  
}
  • addAll方法
public boolean addAll(Collection<? extends E> c) {  
    Object[] a = c.toArray();  
    int numNew = a.length;
    //判断是否扩容  
    ensureCapacityInternal(size + numNew);  // Increments modCount  
    System.arraycopy(a, 0, elementData, size, numNew);  
    size += numNew;  
    return numNew != 0;  
}
  • get方法
public E get(int index) {  
	//范围检查
    rangeCheck(index);  
	//返回数组指定的索引
    return elementData(index);  
}
  • set方法
public E set(int index, E element) {  
	//范围检查
    rangeCheck(index);  
	//获取索引位置的元素
    E oldValue = elementData(index);  
    //赋值
    elementData[index] = element;  
    return oldValue;  
}
  • remove方法
public E remove(int index) {  
	//检查索引
    rangeCheck(index);  
	//记录修改
    modCount++;  
    E oldValue = elementData(index);  
	//删除移动的元素数量
    int numMoved = size - index - 1;  
    if (numMoved > 0)  
	    //移动
        System.arraycopy(elementData, index+1, elementData, index,  
                         numMoved);  
    elementData[--size] = null; // clear to let GC do its work  
  
    return oldValue;  
}

public boolean remove(Object o) {  
	//删除null
    if (o == null) {  
        for (int index = 0; index < size; index++)  
            if (elementData[index] == null) {  
                fastRemove(index);  
                return true;  
            }  
    } else {  
        for (int index = 0; index < size; index++)  
            if (o.equals(elementData[index])) {  
                fastRemove(index);  
                return true;  
            }  
    }  
    return false;  
}
  • subList方法
public List<E> subList(int fromIndex, int toIndex) { 
	//检查索引
    subListRangeCheck(fromIndex, toIndex, size);  
    //创建内部类SubList
    return new SubList(this, 0, fromIndex, toIndex);  
}

//内部类
private class SubList extends AbstractList<E> implements RandomAccess {  
    private final AbstractList<E> parent;  
    private final int parentOffset;  
    private final int offset;  
    int size;  
	//构造方法
    SubList(AbstractList<E> parent,  
            int offset, int fromIndex, int toIndex) {  
        this.parent = parent;  //指向原集合
        this.parentOffset = fromIndex;  //原集合开始索引
        this.offset = offset + fromIndex; //原集合结束索引 
        this.size = toIndex - fromIndex;  
        this.modCount = ArrayList.this.modCount;  
    }
    public E set(int index, E e) {  
    rangeCheck(index);  
    checkForComodification();  
    E oldValue = ArrayList.this.elementData(offset + index);  
    ArrayList.this.elementData[offset + index] = e;  
    return oldValue;
    //....  
}

1、可以看出subList实际上还是指向的原始集合,当原始集合改变或者sublist进行改变,它们的本质是相通的,都是改变的数组

ArrayList<Object> list = new ArrayList<>();  
list.add("a");  
list.add("b");  
list.add("c");  
  
List<Object> subList = list.subList(0, 2);  
subList.set(0,"a1");  //sublist改变原集合也改变
list.set(1,"b1");  //原集合改变,sublist也进行改变
subList.stream().forEach(System.out::println);  
list.stream().forEach(System.out::println);

/**
a1
b1

a1
b1
c
*/

2、LinkedList源码

LinkedList内部维护双向链表的数据结构,他分别有一个节点类Node,以及两个引用,一个头引用,一个尾引用

  • node类

transient Node<E> first; //头引用
transient Node<E> last; //尾引用

private static class Node<E> {  
    E item;  //值
    Node<E> next;  //指向下一个节点
    Node<E> prev;  //指向上一个节点
  
    Node(Node<E> prev, E element, Node<E> next) {  
        this.item = element;  
        this.next = next;  
        this.prev = prev;  
    }  
}
  • 构造方法
//空参构造
public LinkedList() {  
}
//添加集合
public LinkedList(Collection<? extends E> c) {  
    this();  
    addAll(c);  
}
  • add、addAll
public boolean add(E e) {  
	//获取最后一个元素
    linkLast(e);  
    return true;  
}

void linkLast(E e) {  
    final Node<E> l = last; //获取最后一个元素 
    final Node<E> newNode = new Node<>(l, e, null); //创建新节点,新节点的上一个元素只想last 
    last = newNode;  //把尾指针指向新节点
    if (l == null)  //如果尾节点为null,即现在为空链,头指针也指向新建的节点
        first = newNode;  
    else  
        l.next = newNode;  //令老尾节点的下一个节点指向新的尾节点
    size++;  
    modCount++;  
}

//指定索引进行添加
public void add(int index, E element) {  
	//检查位置索引
    checkPositionIndex(index);  
	//如果插入的位置是最后一个元素,采用尾插法
    if (index == size)  
        linkLast(element);  
    else  
        linkBefore(element, node(index));  
}

//根据索引,查找该节点
Node<E> node(int index) {  
    // assert isElementIndex(index);  
	//判断节点在集合的前半部分还是在后半部分
    if (index < (size >> 1)) {  
        Node<E> x = first;  //前半部分进行头部遍历查找
        for (int i = 0; i < index; i++)  
            x = x.next;  
        return x;  
    } else {  
        Node<E> x = last;  //后半部分进行尾部遍历查找
        for (int i = size - 1; i > index; i--)  
            x = x.prev;  
        return x;  
    }  
}

//succ:要插入的位置
void linkBefore(E e, Node<E> succ) {  
    // assert succ != null;  
    final Node<E> pred = succ.prev;  //获取插入位置的之前的元素
    final Node<E> newNode = new Node<>(pred, e, succ);  //创建新节点,节点前索引指向pred,后索引指向插入位置的元素
    succ.prev = newNode;  //插入位置的元素的前节点指向新建的元素
    if (pred == null)  //如果前节点为null
        first = newNode; //把头节点指向新建的几点 
    else  
        pred.next = newNode;  
    size++;  
    modCount++;  
}

//指定索引位置添加元素
public boolean addAll(int index, Collection<? extends E> c) {  
    checkPositionIndex(index);  //检查索引
  
    Object[] a = c.toArray();  
    int numNew = a.length;  //插入元素的长度
    if (numNew == 0)  
        return false;  

	//succ:要插入的节点,pred:要插入节点的前一个
    Node<E> pred, succ;  
    if (index == size) {  //要插入为最后一个元素
        succ = null;  
        pred = last;  
    } else {  
        succ = node(index);  
        pred = succ.prev;  
    }  
	//循环插入
    for (Object o : a) {  
        @SuppressWarnings("unchecked") E e = (E) o;  
        Node<E> newNode = new Node<>(pred, e, null);  
        if (pred == null)  
            first = newNode;  
        else  
            pred.next = newNode;  
        pred = newNode;  
    }  
  
    if (succ == null) {  
        last = pred;  
    } else {  
        pred.next = succ;  
        succ.prev = pred;  
    }  
  
    size += numNew;  
    modCount++;  
    return true;  
}
  • get、set方法
public E get(int index) {
	//检查索引
    checkElementIndex(index); 
    //node方法遍历获取指定索引位置的元素 
    return node(index).item;  
}

public E set(int index, E element) {  
	//检查索引
    checkElementIndex(index); 
    //获取指定索引的节点 
    Node<E> x = node(index);  
    E oldVal = x.item;  
    x.item = element;  
    return oldVal;  
}
  • remove方法
public E remove(int index) { 
	//检查索引
    checkElementIndex(index);  
    //先node方法根据索引查找节点
    return unlink(node(index));  
}
//删除方法
E unlink(Node<E> x) {  
    // assert x != null;  
    final E element = x.item;  
    final Node<E> next = x.next;  
    final Node<E> prev = x.prev;  
  
    if (prev == null) {  
        first = next;  
    } else {  
        prev.next = next;  
        x.prev = null;  
    }  
  
    if (next == null) {  
        last = prev;  
    } else {  
        next.prev = prev;  
        x.next = null;  
    }  
  
    x.item = null;  
    size--;  
    modCount++;  
    return element;  
}
  • subList方法
//linkedlist 的subList方法是继承abstractList方法的subList
public List<E> subList(int fromIndex, int toIndex) {  
    return (this instanceof RandomAccess ?  //判断是否继承RandomAccess接口,RandomAccess接口是标机接口,标记支持随机访问的集合
            new RandomAccessSubList<>(this, fromIndex, toIndex) :  
            new SubList<>(this, fromIndex, toIndex)); // 
}

//AbstractList内部类
class SubList<E> extends AbstractList<E> {  
    private final AbstractList<E> l;  
    private final int offset;  
    private int size;  
  
    SubList(AbstractList<E> list, int fromIndex, int toIndex) {  
        if (fromIndex < 0)  
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);  
        if (toIndex > list.size())  
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);  
        if (fromIndex > toIndex)  
            throw new IllegalArgumentException("fromIndex(" + fromIndex +  
                                               ") > toIndex(" + toIndex + ")");  
        l = list;  //还是指向原集合
        offset = fromIndex;  
        size = toIndex - fromIndex;  
        this.modCount = l.modCount;  
    }
    //添加方法
	public void add(int index, E element) {  
	    rangeCheckForAdd(index);  
	    checkForComodification();  
	    l.add(index+offset, element);  //调用linkedlist的add方法,对原数组进行更改
	    this.modCount = l.modCount;  
	    size++;  
	}

3、对比ArrayList和Linked List

  • ArrayList内部维护的数组而LinkedList内部采用的链表结构
  • ArrayList的结构使得它支持随机访问,但对于插入元素而言,linkedList要快
    • 头部插入时:LinkedList有绝对的优势,它不需要移动元素和进行扩容,而且查找要插入的位置的时候可以很快的找到;ArrayList头部进行插入的时候,可能需要进行扩容,而且移动会牵扯到数组的移动
    • 中间部分插入:由于LinkedList需要循环遍历查找要插入的位置,所以也会消耗大量时间,具体业务具体分析
    • 尾部进行插入:再不考虑扩容的情况下,arrayList尾部插入有绝对的优势,它不需要创建节点等操作
  • 两者都是按照存入顺序进行存取的,而且可以存入重复的元素