首先可以确定的是ArrayList的底层是数组形式,ArrayList有序且元素不重复,查询效率高,增删效率低。
ArrayList的初始容量为10(当数组中没有添加元素时,初始的容量为0;i =1 时,数组的容量变为10),然后使用Arrays.copyOf方法把老数组里面的数据拷贝到新的数组里面,ArrayList为自动容量扩充选择扩充1.5倍,核心源码如下:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 添加一个元素前,先确保容量是否够
elementData[size++] = e;
return true;
}
//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 如果还是初始化的数组,也返回默认大小10和传入参数的最大值,不然直接返回minCapacity(size+1)
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//判断是否需要扩容,如果需要扩容,则调用grow方法扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)// 如果需要的容量大于现有的数组的容量大小,则进行扩容
grow(minCapacity);
}
// 扩容核心方法
private void grow(int minCapacity) {
/*
将新容量大小设定为之前容量的1.5倍,如果大小还不够,将新容量大小设定为需要的容量的大小。如果新容量大小超过了定义的数组的最大容量,则通过hugeCapacity方法来重新计算新容量大小(确保每次扩容不会太大)。如果需要的容量小于0,就抛出OOM异常。不然,则将新容量设定为最大整数值或者定义的数组最大容量值。
*/
int oldCapacity = elementData.length;// 数组现在的大小(扩容前数组长度大小)
int newCapacity = oldCapacity + (oldCapacity >> 1);// 数组应有的新的容量大小,增加为之前的1.5倍
if (newCapacity - minCapacity < 0) // 如果新计算得到的大小还是小于需要的大小,则令新的容量大小为需要的大小的容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)// 如果计算得到的大小超过了List最大大小,则调用hugeCapacity函数
newCapacity = hugeCapacity(minCapacity);// 保证每次做的扩容不会扩的太大
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {// 当前size=Integer.MAX_VALUE时,再添加,size+1就小于0了
if (minCapacity < 0) // overflow 如果容量大小<0,抛出OOM minCapacity = Integer.MAX_VALUE+x(x>0时,就OOM了)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? // 如果最小容量超过List最大大小,则返回整数最大值,如果没有,则返回List定义的最大值
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
当第一次向ArrayList集合中添加元素时,调用add方法,调用函数ensureCapacityInternal
确保ArrayList
当中的数组长度能够满足需求,不然数组会报数组下标越界异常之后。下面我们进入ensureCapacityInternal方法,这个方法是用来确认底层数组大小是否够用来存放数据,如果不够就需要进行扩容,如上图⬆
可以发现上面会进行判断数组是不是一个空数组,因为第一次无参构造创建ArrayList集合底层就是一个空数组,所以如果发现是一个空数组,那么扩容elementData大小为10 下面还需要进入ensureExplicitCapacity方法去判断是否需要真的扩容:
假设现在插入第11个元素,但是只有10个空间,就进入grow()方法,触发扩容;最后回到add()方法进行添加元素。