一、前言
我们都知道ArrayList底层是用一个数组来存储对象的,但是数组的长度是不可变的。当数组内的空间用完之后,ArrayList是如何扩容(即扩大数组长度)的?什么时候开始扩容的?本章就来探讨下这些问题。
二、关键属性
1. 存储数据的数组:Object[] elementData
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
这个肯定关键,没有这个数组ArrayList就没有办法存储数据了。
2. 当前数组中数据个数:int size
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
3. 空数组:DEFAULTCAPACITY_EMPTY_ELEMENTDATA
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
当使用ArrayList的无参构造函数时,会用到这个空数组,源码如下
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
看到此时elementData了么,是个空数组!不是传说中长度为10的数组哟。
4. 空数组:EMPTY_ELEMENTDATA
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
仅当使用new ArrayList(0)的时候,会用到这个空数组,源码如下
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);
}
}
5. 默认初始化大小(有坑哟,后面四中会提到)
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
6. 数组最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
这个名字也有点歧义,后面会看到数组最大长度可以为:Integer.MAX_VALUE
三、扩容的时机
- 从ArrayList的add()方法说起
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 入参为需要的容量
elementData[size++] = e; // 把元素放到数据中
return true; // 返回插入成功
}
add()方法中,简单三行代码,很明显扩容逻辑在第一行代码中,跟进去继续看。
- 计算最小容量(minCapacity)
private void ensureCapacityInternal(int minCapacity) {
// 看calculateCapacity()这个方法
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算需要的容量是多少
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// new ArrayList()的情况,见二、3
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULT_CAPACITY的值为10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
从add()方法中可以看到,入参minCapacity的值为:size+1。所以calculateCapacity()方法的放回值有两种。
- new ArrayList()创建的ArrayList:10和size+1谁大返回谁
- 其他方式创建的ArrayList:返回size+1
tip:扩容后的数组长度,与minCapacity的值息息相关!
- 何时扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 当最小需要的容量大于当前数组长度时,开始扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
四、扩容细节
- 扩容代码
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新容量为原数组长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 原数组为0的情况
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用Arrays.copyOf()方法,把原数组复制到新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
// newCapacity比最大值还要大的情况,根据minCapacity的值来获取新数组长度
private static int hugeCapacity(int minCapacity) {
// minCapacity数值溢出,抛出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// minCapacity > MAX_ARRAY_SIZE时,数组扩容到Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- 扩容分类(三种情况)
2.1 原数组为0的情况,扩容后的大小为minCapacity
2.2 原数组的1.5倍大于MAX_ARRAY_SIZE的情况,根据minCapacity来判断。
minCapacity > MAX_ARRAY_SIZE:扩容为Integer.MAX_VALUE
minCapacity <= MAX_ARRAY_SIZE:扩容为MAX_ARRAY_SIZE
2.3 其他情况扩容为原数组的1.5倍
五、总结
结合minCapacity的值和三种扩容情况,一共有五种扩容的情况
- 当new ArrayList() 第一次add元素时
minCapacity=Math.max(DEFAULT_CAPACITY, 1)=10,扩容后长度为10 - 当new ArrayList(0) 第一次add元素时
minCapacity=size+1=1,扩容后长度为1 - 当数组的1.5倍大于MAX_ARRAY_SIZE且minCapacity小于等于MAX_ARRAY_SIZE时
新数组扩容到:MAX_ARRAY_SIZE - 当数组的1.5倍和minCapacity(size+1)都大于MAX_ARRAY_SIZE时
新数组直接扩容到:Integer.MAX_VALUE - 其他情况
新数组扩容到:原数组的1.5倍