先给结论:
首先我们要明确ArrayList中维护了一个Object类型的数组elementData,因为elementData这个数组是我们扩容的关键
1、当我们创建ArrayList对象时,如果使用的是无参构造方法,则初始的elementData容量为0,当我们第一次添加元素时,elementData就会扩容到10,如果10不够了,则会扩容为10的1.5倍;即是 10+10/2=15,之后依次类推,容量不够就扩大为原来的1.5倍。
2、当我们使用的是指定大小的构造器时,初始的elementData容量就是我们传入的参数,如果需要扩容,则直接扩容为原来的1.5倍。
使用无参构造器创建ArrayList对象时的扩容步骤如下:
@SuppressWarnings({"all"})
public class ArrayListDemo {
public static void main(String[] args) {
// 先添加10个元素
List list = new ArrayList<>();
for(int i = 1; i <= 10; i++){
list.add(i);
}
// 再添加5个元素
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
}
在创建list对象处打断点,然后进行debug,因为添加到容器中的数据是 基本数据类型 int,因此它会自动的装箱为Integer类型
然后进入add()方法
然后调用ensureCapacityInternal()
该方法中有一个size参数,参数默认值为0,用于记录位置索引,其实就是用来判断是否需要进行扩容,以及元素赋值
然后继续跟进,进入到ensureCapacityInternal()中(如果通过step into 无法进入该方法,那么就需要修改一下设置)
把该选项取消勾选
进入到ensureCapacityInternal()方法后
这时的minCapacity 就是刚才的 size+1,目前为1,还有一个参数最开始我们提到的elementData数组,我们通过它来进行扩容
在使用无参构造器创建ArrayList对象时,该属性已被赋值
给elementData对象赋值的是一个空数组
在 ensureCapacityInternal()方法中,需要调用
calculateCapacity(Object[] elementData, int minCapacity)
首先条件进行判断,第一次显然是成立的,因为大家都是空数组,成立后,通过Math.max()
方法获取最大值,这点返回的是 DEFAULT_CAPACIRY,因为该值默认为10
这点就应该知道了无参构造器第一次扩容为什么为10了
然后继续向下来到 ensureExplicitCapacity(int minCapacity)
modCount++ 用来表示集合修改的次数,然后下面有个判定条件
minCapacity目前为10,而elementData依旧是一个空数组,显然条件成立,因此就需要
调用grow(minCapacity),该方法才会真正的进行扩容!
grow() 方法
目前minCapacity=10,而oldCapacity=0,然后newCapacity=0+0/2=0,第一个if()条件成立,这时 newCapacity = 10
这点有个 MAX_ARRAY_SIZE默认值为下面这么多,显然条件不成立
最后进行扩容,通过Arrays.copyOf(),将原先的数组进行扩容,扩容后的数组原有值不会丢失,但是后面扩容的值全为null ,目前而言,因为是第一次,里面10个元素全为null,该方法有两个参数,第一个参数为扩容的数组对象,第二个参数为扩容后的容量。
进行完扩容操作后我们又回到了我们熟悉的add()方法
此时真正的将元素加入到数组中是 elementData[size++] = e; size默认值为0,因此第一个加入到数组中的元素索引为0,然后size++,下一个元素索引就为1了 ,最后返回true,就完成了元素的第一次添加,并且设置了数组的容量为10。
如果后面容量已满,那么依旧是这个套路进行容量扩充
通过minCapacity 与 数组容量进行大小判定,如果此时元素已经占满10, 那么size = 10,因此 minCapacity = size + 1 = 11,这时 上面的if(11 -10 > 0)条件就成立了(这点的理解就是目前我需要11个容量大小,但是你却只有10个),因此调用grow()方法进行扩容
这时newCapacity = 10 + 10/2 = 15,此时if(15 - 11 < 0) 条件不成立,然后下面一个if() 也不成立,最终elementData就变为容量为15的数组,依次向下类推。。。 以上就是无参构造方法进行扩容的全部操作了,下面就是 有参构造方法的执行步骤!
注意进行debug时,需要将该选项 取消勾选,这样显示的信息就会时完整的信息
有参构造方法进行扩容的步骤如下
根据传入的值执行不同的代码块,大于0时会创建一个指定大小的Object[]数组赋值给elementData,等于0就是一个空数组,小于0抛异常,首先这一步就已经确定了elementData数组的容量大小。
直接来到判断是否需要添加容量的方法中
这时该方法返回的值就不再是默认值10了,而是返回需要的最小容量值 ,第一次为1
假设我们传入的初始值为8,很明显第一次添加元素时if(1 - 8 > 0)是不成立的,因此就不会行扩容,当容量达到8,在向里面添加元素时,此时minCapacity就会变为 8+1=9,这时 if(9-8>0),条件成立,就会调用grow()方法进行扩容,依旧扩大为原来的1.5,即是 8 + 8/2=12容量在满时,依次类推。。。



