ArrayList 是 Java 里面经常使用的一种集合,它的本质就是一个动态数组, 不只知足了数组有序存储的特性,还弥补了数组没法动态扩容的缺陷。 ArrayList 的实现原理基于它内部维护了一个 Object 数组,当 ArrayList 容器中的数量达到必定限度时,会从新 new 一个更大的数组,并将原先数组中的数据拷贝过去。
ArrayList 中比较经常使用的方法就是添加元素 add() 和 获取元素 get(),其中add() 方法比较复杂。因此分析的重心就放在了这个方法上,下面就开始一步一步解剖。
ArrayList 里面一共有三个构造方法, 一个无参构造 和 两个有参构造,重点介绍两个关键的构造方法。
指定容量的有参构造算法
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); } }
无参构造数组
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
public boolean add(E e) { // 1⃣️ 确认容器中的容量, 扩容也就是在这一步进行的 ensureCapacityInternal(size + 1); // Increments modCount!! // 下面的代码就没什么好说的了,把元素放到指定索引上 elementData[size++] = e; return true; }
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的位置。