Java 集合- ArrayList 源码浅析
基本使用
新建一个Java类,以最基本的用法来走一遍 ArrayList 是如何运行的
package demo;
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("demo");
String s = list.get(0);
list.remove(0);
}
}
总共分为4步
- 声明一个 ArrayList
- 添加一个元素
- 获取一个元素
- 删除一个元素
开始逐步分析源码
-
声明一个ArrayList
进入ArrayList的构造函数
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ // ... transient Object[] elementData; // ... private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // ... public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } // ... }
其中,
elementData
为实际上 ArrayList 存储数据的 Object 数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
为一个空数组,所以初始化的结果就是将一个空数组赋值给了实际存储数组的数组,其他的什么也没有做 -
添加一个元素
跟踪代码进入 add 函数,发现 add 函数如下所示
public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
其中
modeCount
的作用是记录对这个 list 的修改次数,包括 add/delete 等操作。通过注释可以发现,这个值的作用是用在迭代器中,如果在迭代的过程中修改了数组,则抛出 ConcurrentModificationException 这个异常,例子如下public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("demo1"); list.add("demo2"); list.add("demo3"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String next = iterator.next(); System.out.println(next); if (next.equals("demo2")) { list.remove(next); } } }
进入最主要的 add 函数,发现接受 3 个参数,当前需要添加的值,目前已经存的数据,当前的长度
private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; }
其中首先判断当前的
elementData.length
和list.size
的大小,如果两个值相等,则意味着elementData
已经被填满,需要对elementData
数组进行扩容,否则直接可以在elementData
中的第 size 位添加元素,并把 size 加1即可首次添加元素,通过上面的构造函数可知,此时的
elementData
为空数组,且当前 size 为0,满足条件,需要对elementData
数组进行扩容,进入grow()
private Object[] grow() { return grow(size + 1); } private Object[] grow(int minCapacity) { int oldCapacity = elementData.length; // 非第一次扩容 if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { int newCapacity = ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, /* minimum growth */ oldCapacity >> 1 /* preferred growth */); return elementData = Arrays.copyOf(elementData, newCapacity); } // 第一次扩容 else { return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; } }
参数 size + 1表示此时需要扩容的最小容量
minCapacity
如果是第一次扩容,进入 else 分支,可以看到是初始化
DEFAULT_CAPACITY
和minCapacity
的最大值,其中DEFAULT_CAPACITY
的值是10 ,于是 ArrayList 的初始化容量为 10 就是从这里得出如果不是第一次扩容,则进入 if 分支,可以看到新的容量是通过
ArraysSupport.newLength()
得出,进入对应的函数public static int newLength(int oldLength, int minGrowth, int prefGrowth) { // assert oldLength >= 0 // assert minGrowth > 0 int newLength = Math.max(minGrowth, prefGrowth) + oldLength; if (newLength - MAX_ARRAY_LENGTH <= 0) { return newLength; } return hugeLength(oldLength, minGrowth); }
可以看到该函数接受三个参数
oldLength
当前长度,minGrowth
最小扩容长度,prefGrowth
最佳长度,此时最佳长度为oldCapacity >> 1
,即当前长度的一半。可以看到通过这个函数,返回的新长度为
prefGrowth
和minGrowth
的最大值再加上当前长度,即当前长度的1.5倍,即 ArrayList 默认的 1.5 倍扩容来源于此。在
hugeLength()
中,对于特别大的扩容数量做了单独处理,即Integer.MAX_VALUE - 8
由此 ArrayList 的 add 流程就基本结束。
-
获取一个元素
public E get(int index) { Objects.checkIndex(index, size); return elementData(index); }
首先检查传入的
index
是否超过size
然后进入
elementData(index)
函数E elementData(int index) { return (E) elementData[index]; }
直接通过获取数组的下标返回所需的值
-
删除一个元素
public E remove(int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @SuppressWarnings("unchecked") E oldValue = (E) es[index]; fastRemove(es, index); return oldValue; }
首先同样的,需要校验传入
index
是否超过size
然后通过下标将需要删除的元素取出
然后进入
fastRemove(es, index)
将该元素删除,进入对应函数private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; }
与 add 同样的,首先需要对
modCount
进行 +1其中
(newSize = size - 1) > i
的作用是判断当前的删除元素是否为最后一个,如果是的话,则需要将该元素的后面所有元素前移一位最后将最后一位置 null