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。
3.ArrayList的add方法在超出当前容量时会扩容,remove方法会通过equals方法比较后移除元素,但不会对数组进行缩容。
4.从源码可以看出ArrayList可以快速的get和set元素,但不适合频繁的add和remove元素,LinkedList则相反。
5.ArrayList不是线程安全的,add和remove、set等修改元素操作时,elementData并未上锁。