集合源码分析01——List——ArrayList源码分析
ArrayList源码分析
- 注意事项
- 底层源码(重点、难点!!!)
使用无参构造器
public class ArrayListSource { public static void main(String[] args) { //使用无参构造器创建arraylist对象 ArrayList list = new ArrayList(); //使用for循环添加第1-10个数据 for (int i = 1; i <= 11; i++) { list.add(i); } //使用for循环添加第11-15个数据 for (int i = 11; i <= 15; i++) { list.add(i); } list.add(100); list.add(200); } }
capacity:容量
- 下面我们使用debug来走一遍使用无参构造器创建ArrayList对象并添加数据(扩容)的流程
一、第一次扩容
- 无参构造器的语句
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
追进去之后可以看到是创建了一个Object类型的空数组——elementData
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2.放入数据,每次放入前都会先对int类型的数做一个装箱操作(ArrayList泛型只能传引用类型)
3.进入到 list.add()方法
这里不是直接赋值,而是
(1)先确定是否要扩容
(2)再进行赋值
4.我们继续追进 ensureCapacityInternal()方法
(1)先判断elementData是否为空数组,如果是的话在DEFAULT_CAPACITY(10)和minCapacity之中取一个最大值赋给minCapacity,第一次扩容为10.
(2)进入到ensureExplicitCapacity(minCapacity)
5.ensureExplicitCapacity(minCapacity)
(1) modCount是用来记录当前集合被修改的次数(以防有多个线程同时修改,如果有的话会抛出一个异常)
(2)比较minCapacity(需要的最小容量)和elementData.length(当前数组的大小),如果结果>0说明当前容量不够,需要扩容
(3)如果满足(2)则执行grow()进行扩容
6.grow()
(1)先将当前数组长度elementData.length存入oldCapacity中
(2)执行int newCapacity = oldCapacity + (oldCapacity >> 1); 这里的移位操作相当于将old的长度*1.5,但是由于第一次传入的长度是0,0*1.5还是0所以第一次没有真正用到这个1.5倍扩容,第二次及其以后扩容才会用到。
(3)if (newCapacity - minCapacity < 0) 如果新的容量还没有做小的大就直接使用minCapacity作为newCapacity
(4)如果过大则执行hugeCapacity(minCapacity)这里先不讨论
(5) 使用elementData = Arrays.copyOf(elementData, newCapacity) 进行真正的扩容,添加10个null
回顾:
7.逐层返回到add()方法在第0个位置添加元素(因为是size++ 后++)
二、第二次扩容
1.前10次都差不多,判断if (minCapacity - elementData.length > 0)为false之后就不走grow()了直接返回,可以看到现在已经有了10个元素
2.前几步同第一次扩容大同小异,我们直接进入到grow()方法
3.grow()
(1)这里的oldCapacity为10所以执行1.5倍扩容
(2)if (newCapacity - minCapacity < 0) 也不成立
(3)所以直接走Arrays.copyOf 添加五个null变成15个元素
三、第三次扩容
可以看到大小变成了22个,因为15+15/2向下取整
使用有参构造器
public class ArrayListSource { public static void main(String[] args) { //使用有参构造器创建arraylist对象 ArrayList list = new ArrayList(8); //使用for循环添加第1-10个数据 for (int i = 1; i <= 10; i++) { list.add(i); } //使用for循环添加第11-15个数据 for (int i = 11; i <= 15; i++) { list.add(i); } list.add(100); list.add(200); } }
1.构造器语句——创建了一个指定大小的数组
this.elementData = new Object[initialCapacity];
2.添加数据(同样需要先装箱这里省略)
3. ensureCapacityInternal
因为第一部的构造器,这里的elementData并不为空 我们可以看一下:
所以不走if
4.ensureExplicitCapacity(minCapacity)
比较minCapacity (当前所需最小容量) elementData.length(当前数组长度),如果不>0证明所需最小容量大于当前容量则扩容,前8次不需要扩容所以不走grow()。
5.第9次的时候进入到grow,走1.5倍扩容语句
可以看到走完的时候已经变成了8+8/4=12
小结:
本文来自博客园,作者:紫英626,转载请注明原文链接:https://www.cnblogs.com/recorderM/p/15750418.html