一、ArrayList的扩容原理及过程
当我们创建了一个ArrayList对象并调用它的add方法
List list=new ArrayList();
list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");
list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");list.add("嘿嘿");//一共添加11个元素,用来测试集合长度如何扩容
按住ctrl键后鼠标点击ArrayList类进入源码,
(1)可以发现它的底层其实是一个Object类型的数组变量elementData,(在此可以大胆猜想我们添加的元素可能就是存入这个数组中)
transient Object[] elementData;
(2)在new ArrayList()对象时,毫无疑问执行的是ArrayList的无参构造方法,具体如下
可以看到将一个名为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空数组赋值给了elementData变量,
到此无参构造方法执行结束,也就是说:new一个无参ArrayList,底层创建的仅仅是一个长度为0的空数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
(3)开始第一个元素,执行add方法 ,代码如下
首先size变量目前还未赋值,因此大小为系统给的初始值0,
size其实代表的就是我们实际放入集合中的元素个数(而非elementData.length)。
接下来执行 ensureCapacityInternal(size + 1)这个方法,就是去检测以下容器(elelmentData数组)长度是否够用,
将size+1的值作为是否触发扩容的标准,这个值满足内在的设计条件,则扩容,不满足就不扩容,表示elementData数组容量还凑合,暂时不加长了。具体代码在下面,将在注释中解释这个过程。
调用add方法添加第一个元素执行的代码
private int size;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 将size+1的值作为判断接下来要不要扩容的标准,具体代码就在下一个方法,
elementData[size++] = e;//扩容后的长度为10的数组elementData,因为是Object类型,因此里面是系统给的初始值全部都为null,
//并将下标为size:0处的null改为元素e:"嘿嘿",然后size+1,即有效元素为1
return true;//添加完毕,返回true
}
//private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//检测size+1的长度是否需要扩容
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {//此时传入的minCapacity=size+1=0+1=1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//此时的elementData={},因此满足这个条件,
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10,所以10和1最大值为10赋值给minCapacity变量
}
ensureExplicitCapacity(minCapacity);//minCapacity=10;
}
protected transient int modCount = 0;
//将10这个长度再拿过来检测
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)//此时elementData依然是一个空数组,因此10-0>0 条件成立,
grow(minCapacity); //开始执行核心扩容方法
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {//minCapacity=10
int oldCapacity = elementData.length;//此时elementData依然是空数组,长度自然为0,因此oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1);```
**>>1 这个表示是二进制右移一位,0右移一位依然是0,因此newCapacity=0+0=0,后面当添加到11个元素时,也会在此扩容,扩容后的长度就是这样子算的, 10+10>>1 右移1位相当于乘以0.5 ,因此newCapacity=10+10*0.5=15 ,也就是说,扩容倍数为1.5倍**
```java
if (newCapacity - minCapacity < 0)//minCapacity:传入方法参数值10, 0-10=-10<0 为true,if成立
newCapacity = minCapacity; //将10赋值给新的数组长度 newCapacity=10
if (newCapacity - MAX_ARRAY_SIZE > 0)// MAX_ARRAY_SIZE =int的4个字节一共32位bit的最大值大概21亿多减去8的值,显然不成立
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//通过Arrays工具类将老数组内容复制到一个新的数组,并指定此数组的长度为newCapacity:10 ,因此grow完成了扩容,返回的是长度为10的数组,里面存了系统给的默认值10个null。
}
总结:对于ArrayList ,new无参对象时,底层是一个空数组,当添加第一个元素时,会进行扩容,将底层数组长度扩为10,
其中扩容触发的条件是:存元素时,即先让size+1的值判断是否大于底层elementData.length的长度,如果大于,则先扩容再添加,
扩容的倍数为1.5倍。