jdk8中ArrayList源码分析
1.构造方法分析
ArrayList初始化时,可以设定数组初始容量,不设定则数组默认初始容量为0,即一个空的对象数组,或者初始化时传入一个集合,集合中的元素会被复制到ArrayList中的对象数组elementData。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默认等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA transient Object[] elementData; // non-private to simplify nested class access /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ 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); } } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
2.Add方法分析
size: ArrayList中已有元素个数
elementData: ArrayList中存放元素的对象数组
简易原理分析:
添加元素时先计算当前需要的容量,如果ArryList没有设置初始容量且是第一次添加元素,则初始化elementData为长度10的数组;
否则比较size+1和当前elementData的长度,若小于等于则无需扩容直接添加,若大于则进行1.5倍扩容;
若1.5倍扩容仍小于size+1,则直接扩容至size+1大小,这种情况仅有一种,那就是设置ArrayList初始容量为1,当第二次添加元素时,size=1时,此时1.5size仍然为1,由于使用Arrays.copyOf(elementData, 1.5倍size),无法直接扩容成功。
方法调用过程:
ArrayList添加元素时,先调用方法ensureCapacityInternal,ensureCapacityInternal中先调用方法calculateCapacity计算当前需要的容量,在将结果传入ensureExplicitCapacity方法,然后ensureExplicitCapacity方法,如果elementData容量足够则无需扩容,若不足则调用grow方法进行扩容。
最后,add方法将元素放进数组末尾。
calculateCapacity方法处理是:
如果elementData还是一个空数组,那么就返回DEFAULT_CAPACITY(值为10)和传入的数minCapacity中大的值;
如果elementData不为空数组,那么就返回传入的数。
ensureExplicitCapacity方法处理是:
如果传入的参数比elementData这个对象数组的长度大,则进行调用grow方法进行扩容;
否则不做处理。
grow方法处理是:
newCapacity 等于elementData数组长度的1.5倍;
如果newCapacity 仍小于传入的参数,那么newCapacity 等于传入的参数,这种情况仅适用于一种,那就是size=1时,此时1.5size仍然为1,即newCapacity =1,此时直接使用Arrays.copyOf达不到扩容效果;
如果newCapacity >MAX_ARRAY_SIZE (最大数组容量),那么调用hugeCapacity方法;
最后使用Arrays.copyOf方法,将elementData复制到一个newCapacity 长度的新数组并赋值给elementData。
hugeCapacity方法处理是:
如果minCapacity<0,就抛OutOfMemoryError异常,即内存溢出;由于minCapacity是int型,那么会导致minCapacity<0的,只有计算minCapacity时,size+1<0或者size+1>2^31-1这两种情况。
如果minCapacity>MAX_ARRAY_SIZE,则返回Integer最大值(2^31-1);
否则返回MAX_ARRAY_SIZE(2^31-1-8)
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++;//结构修改次数 // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); 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); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
下面这种带索引的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++; } private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
3.Remove方法分析
按index移除元素:
首先检查index范围,然后计算需要移动的元素个数,使用System.arraycopy方法从下标为index+1的的元素以及后面的元素都向前移动1位。
接着--size,接着把elementData的size位置为null,正好是未修改之前数组的第size个元素置为null。
elementData数组这里并没有缩容。
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; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
按object移除元素
1.如果object为空,遍历elementData,找到第一个为null的object并移除,elementData数组这里并没有缩容。
2.如果object不为空,遍历elementData,找到第一个object.equals==true的并移除该元素,同样的elementData数组这里并没有缩容。
public boolean remove(Object o) { 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; } private void fastRemove(int index) { modCount++; 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 }
4.get/set方法
代码比较容易懂。
public E get(int index) { rangeCheck(index); return elementData(index); } public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
总结:
1.ArrayList可以通过构造方法设置初始化容量,也可以直接传入集合,此时初始化容量为集合大小。
2.ArrayList默认初始化容量为0,第一次使用add添加元素时如果未设置初始化容量,则默认会扩容至10,当容量不足时会自动扩容,每次扩容为当前容量的1.5倍,仅当容量为1时,会扩容当前容量为2。
2.ArrayList的add方法在超出当前容量时会扩容,remove方法会通过equals方法比较后移除元素,但不会对数组进行缩容。
3.从源码可以看出ArrayList可以快速的get和set元素,但不适合频繁的add和remove元素,LinkedList则相反。
4.ArrayList不是线程安全的,add和remove、set等修改元素操作时,elementData并未上锁。