ArrayList源码中是用一个数组(Object[] elementData)来保存元素的,自然,三个构造函数都会为elementData赋值。最简单的构造函数如下:
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
从注释可以看出, 构造一个初始容量为10的空列表 。那么当放入第十一个元素时,程序猿并没有去主动扩充elementData的数组长度,这个扩容过程是源码完成的。那么,源码是怎么完成扩容的?一次扩容多少?
在ArrayList源码中,方法名带有“add”的方法都会调用ensureCapacityInternal(int minCapacity)方法,ensureCapacityInternal方法是保证数组长度够用。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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 int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在调用add方法时,调用ensureCapacityInternal方法,传入的参数为 size + 1,。size是ArrayList的大小,即ArrayList的实例中含有多少个元素,注意size并不是elementData数组的长度。成员变量size上面的注释The size of the ArrayList (the number of elements it contains).
ensureCapacityInternal方法调用了ensureExplicitCapacity方法,ensureExplicitCapacity方法又调用了grow方法,当minCapacity值大于elementData.length时,又调用了grow方法。在扩容的时候,源码是先获取当前elementData长度,在oldCapacity 基础上加上 oldCapacity 的1/2(oldCapacity >> 1 相当于oldCapacity除以2),这就是扩容ArrayList 扩容1.5倍说法的由来。但是,我们可以看到下面有两个if语句,第一个if表示,当扩容1.5倍时,还是不能满足这次的“add”操作,(这里是考虑addAll方法,一次添加多个元素的情况),就使用minCapacity作为新的数组长度。
第二if 表示当 newCapacity大于MAX_ARRAY_SIZE时,MAX_ARRAY_SIZE的数值为Integer.MAX_VALUE - 8;为什么是Integer的最大值减去8,这个问题的解释比较多,stackoverflow上回答算比较好的解释。我个人认为MAX_ARRAY_SIZE的注释,其实已经回答得差不多了——数组能分配的最大的大小,一些VMs会在这个数组中保留一些header words,试图分配更大值(也就是说分配Integer.MAX_VALUE - 7到Integer.MAX_VALUE 这7个值,严格来说,数组最大的长度只能是Integer.MAX_VALUE)会导致OutOfMemoryError:即请求的数组大小超出VM限制。具体header words指的是什么,有兴趣的话,可以进一步研究学习。
hugeCapacity方法里面的抛出OutOfMemoryError,是可能发生的。因为int的最高位是表示符号的,0表示正数,1表示负数,不妨执行一下System.out.println(Integer.MAX_VALUE + 1)看看会输出什么。
那么讲完扩容,讲讲缩容。如果一个ArrayList实例删除了1/2或更多,会不会减小elementData的数组长度。源码并不会主动缩容,但可以使用 trimToSize 方法缩容。