本篇文章以无参构造方法构建ArrayList的过程说明扩容机制,即
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
看一下用到的成员变量:
// 默认容量,DEFAULTCAPACITY_EMPTY_ELEMENTDATA用
private static final int DEFAULT_CAPACITY = 10;
// 无参构造器初始化用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList中拥有元素的数目
private int size;
初始化
从构造器说起:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
在使用无参构造器初始化ArrayList的时候,ArrayList存储数据的内部数组被初始化名为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的数组,这个数组定义如下:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
这是个容量为DEFAULT_CAPACITY的数组,DEFAULT_CAPACITY的默认值为10,在这里看不出这个数组初始容量为10,在下面的流程中就会看见,为什么这个数组初始容量为10.
添加元素与扩容
看list.add(1)所调用的add方法的源码:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
这个方法将元素添加到ArrayList的末尾位置。
进入add方法,ensureCapacityInternal就是来确保当前ArrayList拥有足够的空间,其源码如下:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
其参数是minCapacity,表示需求的最小的容量,即size+1,其中size表示当前ArrayList中存储的元素的个数,添加一个元素,即size+1可以保证最小要求。看ensureCapacityInternal内部,是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;
}
该函数首先判断elementData是否和DEFAULTCAPACITY_EMPTY_ELEMENTDATA相等,这就是在初始化时使用的数组,如果相等,那就返回DEFAULT_CAPACITY和minCapacity(即size+1)中的较大者。这也就是为什么DEFAULTCAPACITY_EMPTY_ELEMENTDATA初始化容量为10. 如果ArrayList的size为0,即拥有0个元素,添加一个元素后,size+1=1,此时,10 > 1,返回的就是10。即ArrayList的初始容量为10. 返回容量后,该参数传入ensureExplicitCapacity,看该函数的源码:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这个函数内部,比较了计算得到的容量(即我们需要的容量)和elementData这个ArrayList内部数组容量大小,如果前者大于后者,说明容量不够,需要进行扩容,这是调用grow方法。即扩容的核心函数:
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);
}
先使用oldCapacity保存旧容量,newCapacity保存新容量,oldCapacity + (oldCapacity >> 1)即原来容量的1.5倍,>>符号二进制右移1位,表示除以2。这种写法运算速度快,也有防止越界的功能。具体可以进行进一步的查询。接下来会有两个判断,第一个判断新容量是否比旧容量还小,这个就新容量就赋值为旧容量。第二个判断是看新容量是否超过了最大数组容量,如果超过了,调用hugeCapacity函数,这个函数如下:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
这个函数要么返回最大值(超过),要么返回计算得到的容量(没超过)。在确定新容量之后,就使用copyOf方法,将旧 数组内容复制到具有新容量的数组中,完成扩容。
以上便是使用无参构造器创建ArrayList,然后进行扩容的过程。其它方式,如指定初始大小,或者使用集合作为初始化参数,可自行分析下。