ArrayList源码分析

以前,我刚开始学习JavaSe时,还不知道有ArrayList类。刚开始存多个元素时,都是使用数组。但感觉使用数组,不是那么方便。因为Java和php不一样(但和C一样),数组从定义起,其长度就被确定了,不能再被修改了。如果给数组长度定的太大,那么多余的空间浪费感觉挺可惜,但如果给小了,程序就会出问题。

等学习到了集合时,知道了有ArrayList,使用它时,我们不用费心思考虑到底给多个的容量,每次add时,它的容量会自动加1。

今天通过解读源码的方式,来说说ArrayList是如何进行扩容的。测试代码非常简单

ArrayList<String> hobbys = new ArrayList<>();

hobbys.add("动漫1");
hobbys.add("动漫2");
hobbys.add("动漫3");
hobbys.add("动漫4");
hobbys.add("动漫5");
hobbys.add("动漫6");
hobbys.add("动漫7");
hobbys.add("动漫8");
hobbys.add("动漫9");
hobbys.add("动漫10");
hobbys.add("动漫11");

当我们使用空参构造器创建实例时,会进行以下操作。

this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

注意这个elementData,它就是用来保存元素的容器的。该变量类型是一个数组。

DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空数组。

所以,这个时候,elementData数组的长度和元素个数都为0。

接下来执行

hobbys.add("动漫1");    

在Arraylist的add方法体内容如下:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

ensureCapacityInternal(int length),方法用来确保elementData数组长度不能小于length。如果当前数组长度小于length的话,会执行内部扩容操作。

size属性初始值为0,它用来表示容器内的元素个数。

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

当elementData数组长度为0时,将会执行

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

这个时候minCapacity长度为DEFAULT_CAPACITY,该值为10.

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

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

minCapacity肯定比elementData.length大,所以执行grow(minCapacity)。这个方法是真正执行扩容的方法。

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

一般情况下,扩容的大小为原来长度的1.5倍。但是,刚开始的情况下,elementData的长度为0,扩容1.5倍还是0。所以,会拿该值和最小默认扩容值(10)进行比较。取两者中大的值进行扩容。

而最最核心的扩容代码如下

elementData = Arrays.copyOf(elementData, newCapacity);