一、源码如何分析?
1.成员变量
2.构造方法
3.关键方法
一些添加的方法。
二、debug看源码
我们给出下面代码:
public void test01() {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
for (int i = 2; i <= 10; i++) {
list.add(i);
}
list.add(11);
}
1.第一次添加数据
1. 添加方法:
2. 确保内部容量:
3. 计算容量:
4. 确保真正的容量:
5. 扩容的方法:
围绕着这5个方法进行讲解:
首先,add()方法里面先执行ensureCapacityInternal(size + 1); 这里的size是0,因为数组是空的,所以传入到ensureCapacityInternal()方法的参数就是minCapacity == 1 。
其次,我们就需要计算容量,在calculateCapacity()方法中,里面的传入的两个参数,其中第一个参数elementData的值,在ArrayList() 构造方法里面已经赋过值了,就是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,第二个参数就是前面的 1 。所以if里面判断正确,返回最大值,其中DEFAULT_CAPACITY = 10,所以10>1,最后返回的也就是10。
然后,我们在ensureExplicitCapacity()中这个方法,传入的minCapacity这个参数也就是 10。因为在if判断中,条件成立,也就是说容量不够,需要扩容了。
最后,进入grow()方法,其中因为oldCapacity=0,newCapacity计算得出也是0。第一个条件判断就是0-10<0,所以把10赋值给newCapacity,第二个是数组容量最大的安全校验(暂时不用),最后一行代码也就是数组的拷贝,把elementData的容量变成了 10 。
这样,我们的数组第一次扩容就这样说完了,我们回到add方法,elementData[size++] = e,这行代码就是把下标为0的位置给了元素1,然后size+1,说明数组有一个元素了。
2.第二次添加数据
我们第二次添加是用for循环,添加了9个元素,我们分析这个过程。
首先,for循环添加第一个元素的时候,ensureCapacityInternal(size + 1); 这里的size是1,因为数组之前添加了一个元素进行了size++,所以传入到ensureCapacityInternal()方法的参数就是minCapacity == 2。
其次,在计算容量的时候,elementData已经不是默认的了,所以直接返回minCapacity。
然后,我们在ensureExplicitCapacity()中这个方法里面,在if判断里面,其中minCapacity是等于2的,elementData.length在第一次添加数据的时候,已经扩容为10了,所以2-10<0,不需要扩容了。在代码里面,for循环添加了9个元素,执行流程都一样。
最后,在add方法里面,把数据添加到数组后进行size+1。
3.第三次添加数据
我们第三次添加一个元素,其中数组已经有10个元素了,我们分析这个过程。
首先,ensureCapacityInternal(size + 1); 这里的size是10,因为数组里面有了10个元素,所以传入到ensureCapacityInternal()方法的参数就是minCapacity == 11。
其次,在计算容量的时候,elementData已经不是默认的了,所以直接返回minCapacity。
然后,我们在ensureExplicitCapacity()中这个方法,传入的minCapacity这个参数也就是 11。因为在if判断中,条件成立11-10>0,也就是说容量不够,需要扩容了。
最后,进入grow()方法,其中因为oldCapacity=10, oldCapacity >> 1也就是右移一位也就是5,newCapacity计算得出是15。第一个条件判断就是15-10>0,直接走过该判断,最后一行代码也就是数组的拷贝,把elementData的容量变成了 15 。
这样,我们的数组扩容就完成了。
三、面试题
1. ArrayList底层的实现原理是什么?
-
底层数据结构
ArrayList底层是用动态的数组实现的
-
初始容量
ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10
-
扩容逻辑
ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组
-
添加逻辑
-
确保数组已使用长度(size)加1之后足够存下下一个数据
-
计算数组的容量,如果当前数组已使用长度+1后的大于当前的数组长度,则调用grow方法扩容(原来的1.5倍)
-
确保新增的数据有地方存储之后,则将新元素添加到位于size的位置上。
-
返回添加成功布尔值。
-
2. ArrayList list=new ArrayList(10)中的list扩容几次?
该语句只是声明和实例了一个 ArrayList,指定了容量为 10,未扩容。