当第一次调用add方法添加元素时,方法调用流程
1、ensureExplicitCapacity(int minCapacity) {…}方法,判断是否需要扩容,其中:
modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity);
minCapacity表示预期的数组最小容量值,elementData.length表示的当前实际数组长度,如果是空参构造方法去创建集合,那么可知此时传入到这个方法的minCapacity为10,而实际一开始我们创建的空集合List arrList = new ArrayList(),长度为0,那么这个判断为true,“表示现在向list集合add元素,底层数组已经无法存储更多的元素了”。所以会进入grow方法进行扩容。minCapacity - elementData.length > 0,可以理解为当前数组是否能存放下minCapacity个元素。
特别注意这里针对的是ArrayList的无参构造。此时的elementData是我们初始化时的空数组,数组长度为0,只有在add元素时才会扩容为10,在没有完成grow方法前,所有代码中涉及的elementData数组都是个空数组,length为0。
2、至于grow(minCapacity)方法就比较好理解了, int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;在完成第一次向空数组添加元素后 ,完成第一次数组扩容后的elementData.length 已经为10了 ( 第一次add时,if(0-10 < 0) 为返回true, newCapacity = minCapacity = 10)。当添加第11个元素时,这个if(15-10<0)判断不成立,进行扩容的新数组长度newCapacity 为15。这两段代码说明了为什么会扩为原来数组长度的1.5倍,“oldCapacity >> 1”可表示为oldCapacity / 2。后面还会判断newCapacity 与MAX_ARRAY_SIZE(最大数组长度)的大小,调用newCapacity = hugeCapacity(minCapacity);这个就不分析,MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,也就是2147483647 -8。以上就确定了新数组的长度newCapacity,最后调用elementData = Arrays.copyOf(elementData, newCapacity);实际是调用System.arraycopy()完成新数组的创建。
总结就是:以无参构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组扩容为10。
3、另外ArrayList提供一个对外可调用的方法, ensureCapacity方法
该方法中 :
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; //判断elementData是否是通过无参构造的(空数组),是 取反则为 false 返回 DEFAULT_CAPACITY 默认值为10
否则取反为 true 返回0。
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
//这个地方minCapacity为指定的容量,minExpand可以理解为是最小需要扩充的容量(据上述分析,用户可以指定数组的长度,这个方法是显示的去分配底层数组容量,以减少递增式再分配的次数)。