集合源码分析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对象并添加数据(扩容)的流程

一、第一次扩容

  1. 无参构造器的语句  
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

 小结:

 

posted @ 2021-12-30 20:17  紫英626  阅读(36)  评论(0编辑  收藏  举报

紫英