ArrayList源码

124 阅读3分钟
  • 持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

构造方法

ArrayList有三个构造方法,默认的无参构造跟传递数值参数和Collection类参数的有参构造

无参构造

无参构造方法下会指向一个默认的空数组,所以并不是new的时候就分配了空间

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

数值参数构造方法

传参为数值的构造方法在参数大于0的情况下会创建一个对应长度的数组,如果等于零会指向一个静态常量的空数组,剩下的可能就是小于0,则会抛异常


/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

异常

image.png

Collection构造方法

这个构造方法可以传递所有Collection及它下面的子类,有560个

image.png

在这个构造方法里会先将传递的参数Collection转为对象数组,

Object[] a = c.toArray();

然后判断新数组大小是否等于0,

if ((size = a.length) != 0) 

不等于0的话会判断Collection是不是ArrayList,如果是ArrayList就将数组直接指向新数组,否则就需要对Collection进行复制操作生成一个ArrayList对象来达到转换的目的。

if (c.getClass() == ArrayList.class) {
    elementData = a;
} else {
    elementData = Arrays.copyOf(a, size, Object[].class);
}

等于0跟上面的情况一样,指向一个静态常量的空数组对象

// replace with empty array.
elementData = EMPTY_ELEMENTDATA;

完整代码

image.png

add

image.png 在ArrayList进行添加操作时会先对数组进行初始化或者扩容

获取数组长度

ensureCapacityInternal(size + 1);  // Increments modCount!!

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

注意下面的这个方法,这个方法是提供数组长度的方法,在进行add操作的时候如果当前数组为空数组会比较传参的长度取最大值,这样就可以说ArrayList无参构造方法创建的时候并没有对长度初始化,而是在进行add操作的时候进行的长度初始化,默认值为10,不为空的情况直接返回传入参数的值

calculateCapacity(elementData, minCapacity)
private static final int DEFAULT_CAPACITY = 10;

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

增加modCount并判断是否扩容

calculateCapacity这个方法就是增加modCount的操作跟判断是否需要扩容的逻辑了

modCount是一个记录List对象被修改多少次的值

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

modCount++不用看,就是增加一下修改次数,看一下下面的判断逻辑,如果传入的需要更改的数组长度减去当前数组的长度还大于0就需要扩容了

扩容逻辑

image.png

运算逻辑是先拿当前的长度得到旧长度,然后将当前数组长度的1.5倍当做新长度,这里使用的位运算

int newCapacity = oldCapacity + (oldCapacity >> 1);

得到新长度后判断新长度减去传递的参数长度小于0,小于0的话直接用参数长度即可,不小于判断是不是比设置的最大长度大,查看达到最大数值的判断逻辑

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

如果传入参数小于0抛异常这是兜底性校验,然后三元表达式判断传入参数值是否大于最大列表长度,最大列表长度是Integer.MAX_VALUE - 8 如果大于只能上Integer.MAX_VALUE否则就还是MAX_ARRAY_SIZE

完成后按长度进行列表扩容