ArrayList 源码分析

1. 成员属性

     // 默认的容量
    private static final int DEFAULT_CAPACITY = 10;

    // 空元素时,对象数组的指向    
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 默认的为空时的指向,用于无参构造方法初始化
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // ArrayList底层,实际的存储
    transient Object[] elementData; // non-private to simplify nested class access

    // 实际存储的对象的数量 int类型,默认为0
    private int size;

2. 构造方法

a. 无参构造

image-20210501105917222

无参构造方法很简单,仅仅是将对象数组指向了默认的空数组.

b. 有参构造

image-20210501110058622

有参构造即传入初始化的容量,如果初始化的容量大于0,则将数组对象指向一个新的容量为initialCapacity的对象数组,如果容量等于0,则将对象数组指向空数组EMPTY_ELEMENTDATA,而如果小于0,则直接抛异常不合法.

c. 有参构造(Collection集合)

image-20210501110350823

传入Collection集合为参数的构造方法也简单,首先将传入的集合toArray()(返回一个包含所有集合里有的Object数组),在判断对象数组的大小,如果大于0,则判断传入的Collection集合是否是Object类型的数组,不是则转换一遍(这里也能看到一点类型擦除的影子).否则则将对象数组赋值为空对象数组.

3. 添加元素

添加元素为add()方法:

image-20210501110745559

可以看到,第一步就先判断了容量是否符合

image-20210501110827801

此方法又调用了另外两个方法,

calculateCapacity用于计算扩容的容量:

image-20210501110927409

首先判断是不是为默认的空对象数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),如果是,则表明我们当前实际存储的对象数组是一个空数组,那么我们就取默认大小和minCapacity的最大值,而DEFAULT_CAPACITY为10,如果为空数组,理论上此处minCapacity为1,因此会返回10.也就对应了我们的无参构造方法,无参构造方法初始化时,先不会给对象数组分配空间,而是让它指向默认的空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),直到第一次添加元素时,会将它默认的初始化成大小为10的对象数组.

否则就直接返回需要扩容的大小

ensureExplicitCapacity方法则是判断是否需要扩容的

image-20210501111528703

获取需要扩容的大小后,根据判断minCapacity(存储新值需要的大小)减去实际数组的大小,如果大于0,表示当前数组无法继续容纳了,需要扩容,传入当前需要的容量大小.

真正的扩容逻辑,在grow方法里面

image-20210501111734565

首先获取了原容量,在计算新容量,新容量为原容量的1.5倍(右移1位等于乘2),如果新容量比当前需要的容量还要小,那么我们的计算值无效,直接将新容量的值改为我们当前需要的容量大小,而如果当前的新容量大于了最大的数组容量(整数的最大值-8),那没办法,我直接给你申请最大整数个容量的数组,再大我就直接给你OOM了.

image-20210501112426792

MAX_ARRAY_SIZE值为Integer.MAX_VALUE-8原因与对象数组的大小在某些虚拟机可能存储在对象头上有关,大于这个值则有可能出现OOM.

在判断完需要扩容后,通过Arrays.copyOf方法将原数组赋值过来

而为什么新容量为原容量的1.5倍,主要为了方便后续使用,空间换取时间,不然每次添加元素就扩容一遍?

最后在通过elementData[size++] = e;方法直接完成赋值

4. 移除元素

移除元素比较简单,主要就是通过计算索引的边界,然后通过System.arraycopynative方法直接copy一份新的出来.

image-20210501154208064

Last modification:May 19, 2022
If you think my article is useful to you, please feel free to appreciate