ArrayList 源码分析

开篇

ArrayList 是 Java 里面经常使用的一种集合,它的本质就是一个动态数组, 不只知足了数组有序存储的特性,还弥补了数组没法动态扩容的缺陷。  	
ArrayList 的实现原理基于它内部维护了一个 Object 数组,当 ArrayList 容器中的数量达到必定限度时,会从新 new 一个更大的数组,并将原先数组中的数据拷贝过去。

源码分析(基于 jdk1.8.0_191 版本)

ArrayList 中比较经常使用的方法就是添加元素 add() 和 获取元素 get(),其中add() 方法比较复杂。因此分析的重心就放在了这个方法上,下面就开始一步一步解剖。

几个重要属性

  • DEFAULT_CAPACITY 默认容量
  • elementData ArrayList 内部维护的数组
  • EMPTY_ELEMENTDATA 一个空数组,被全部的空实例共享
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA 也是一个空数组, 与上面的区分开来。以便肯定当添加了第一个成员以后扩充多大
  • size ArrayList 存放元素的个数

构造方法

ArrayList 里面一共有三个构造方法, 一个无参构造 和 两个有参构造,重点介绍两个关键的构造方法。
  1. 指定容量的有参构造算法

    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);
        }
    }
  2. 无参构造数组

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

add() 方法

public boolean add(E e) {
    // 1⃣️ 确认容器中的容量, 扩容也就是在这一步进行的
    ensureCapacityInternal(size + 1);  // Increments modCount!!
     // 下面的代码就没什么好说的了,把元素放到指定索引上
    elementData[size++] = e;
    return true;
}

ensureCapacityInternal() 方法

private void ensureCapacityInternal(int minCapacity) {
     // 2⃣️ 下面分析 calculateCapacity
    ensureExplicitCapacity(calculateCapacity(elementData,   minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 若是经过 ArrayList 建立的实例, 那么在添加第一个元素时, 会获得一个容量为 10 的 ArrayList 实例
    // 第二次添加元素时,就不会走这一步了。
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // DEFAULT_CAPACITY = 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //  若是经过 ArrayList(initCapacity), 指定了初始容量,哪怕是 0 就直接返回。
    return minCapacity;
}

//  ③ 下面会拿一个空的容器来解释 也就是 经过new ArrayList()建立容器实例
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;// 这个变量能够先无论, 他表示 容器中 size 变化的次数。

    // 当添加第 1 个元素时, elementData.length的长度 为 0, minCapacity 为10,因此最终会建立一个容量为 10 的 ArrayList
    // 当添加第 2 个元素时, minCapacity为 1 ,elementData.length 为10, 无需扩容。
    // ......
    // 当添加第 11 个元素时,minCapacity为 11, elementData.length 为10, 会扩容1.5倍
    if (minCapacity - elementData.length > 0)
        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);
    // 拷贝数组,并添加新元素
    elementData = Arrays.copyOf(elementData, newCapacity);
}

总结

1. 当经过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会建立一个长度为10的数组,并将该元素赋值到数组的第一个位置。
2. 第 2 次添加元素,集合不为空,并且因为集合的长度 size+1 是小于数组的长度 10,因此直接添加元素到数组的第二个位置,不用扩容。
3. 第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候建立一个长度为10+10*0.5 = 15 的数组(扩容1.5倍), 
而后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
相关文章
相关标签/搜索