ArrayList源码解析!

ArrayList整体架构是一个数组结构:

基本概念:

DEFAULT_CAPACITY:表示数组的初始大小,默认是 10。

size:表示当前数组的大小,类型是 int,没有使用 volatile 修饰,非线程安全。

modCount:统计当前数组被修改的版本次数,数组结构有变动,就会加 1。

初始化

ArrayList无参构造器初始化时,默认大小是空数组,并不是默认的DEFAULT_CAPACITY 10,10 是在第一次 add 时扩容的值。

新增

判断是否需要扩容,如果需要则执行扩容操作。

赋值新元素。

1
2
3
4
5
6
7
public boolean add(E e) {
// 确保数组大小是否足够,不够则执行扩容,size 为当前数组的大小
ensureCapacityInternal(size + 1); // Increments modCount!!
// 直接赋值
elementData[size++] = e;
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 计算 capacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果数组为空,则返回默认容量(10) 和最小容量(比如初始化 List add 一个 element 时 minCapacity 为 1)之间的 max 值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

// 确保容量足够
private void ensureExplicitCapacity(int minCapacity) {
// 记录数组被修改
modCount++;

// 如果我们期望的最小容量大于目前数组的长度,则执行扩容操作
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

// 扩容,并把现有数据拷贝到新的数组中去
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新的容量为旧的容量的 1.5 倍(capacity >> 1 ==> capacity / 2)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新的容量小于我们期望值 minCapacity,则将新的容量值赋值为期望值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新的容量大于 JVM 所能分配的数组的最大值,那么就用 Integer 的最大值
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 倍)。

ArrayList 中数组的最大容量是 Integer.MAX_VALUE,超过这个值,JVM 就不会给数组分配内存空间了。

新增时,并没有对值进行严格的校验,所以 ArrayList 是允许 null 值的。

扩容

扩容会先新建一个符合我们预期容量 newCapacity 的新数组,然后把旧数组的数据拷贝过去。

1
2
3
4
5
6
7
8
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

线程安全

只有当 ArrayList 作为共享变量时,才会有线程安全问题,当 ArrayList 是方法内部局部变量时,是没有线程安全问题的。

本质是因为 ArrayList 自身的 elementData、size、modCount 在进行各种操作时,都没有加锁。

而且这些变量的类型并非是可见的(volatile)的,所以如果多个线程对这些变量进行操作时,可能会有值被覆盖的情况。