1. 概述

1.1 说明

作者小白一枚, 水平有限, 如有错误还望各位指正.

本文章中源码来自: JDK8.

1.2 简介

ArrayList 是容量可变的非线程安全列表, 使用数组实现. 适用于查询为主的场景, 提供了增加, 删除, 更改, 遍历的方法.

1.3 继承关系

RandomAccess: 标记接口, 标记实现该接口的集合使用索引遍历比迭代器更快.

Serializable: 标记接口, 标记实现该接口的类可以序列化.

Cloneable: 标记接口, 标记实现该接口的类可以调用 clone 方法, 否则会抛出 CloneNotSupportedException (克隆不被支持)异常.

Iterable: 实现此接口允许对象成为 “for-each循环” 语句的目标.

Collection: Java 集合体系的根接口, 重写了 Iterable 接口的 spliterator 方法, 定义了集合基本的待实现方法.

AbstractCollection: 重写了 Conllection 接口中基础的方法, 减少具体集合类的实现成本. 部分方法需要具体集合自我实现, 如 add 方法.

List: 重写了 Collection 接口的 spliterator 方法, 定义了 sort, copyOf, replaceAll, of 方法. 添加了一些待实现方法, 如: addAll, get, set等.

AbstractList: 重写了 List 中的大部分方法, 作用与 AbstractCollection 类似.

2 源码

2.1 字段

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
   /**
* 序列化版本标识,序列化和反序列化时使用
*/
private static final long serialVersionUID = 8683452581122892189L;

/**
* 默认初始容量.
*/
private static final int DEFAULT_CAPACITY = 10;

/**
* 用于 ArrayList 空实例的共享空数组实例.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
* 用于默认大小空实例的共享空数组实例.
* 我们将其与 EMPTY_ELEMENTDATA 区分开来, 以了解添加第一个元素时应该膨胀多少
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
* 存储 ArrayList 元素的数组缓冲区.
* ArrayList 的容量是此数组的长度.
* 当添加第一个元素时, 任何 emementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将被扩展为 DEFAULT_CAPACITY.
*/
transient Object[] elementData; // non-private to simplify nested class access

/**
* ArrayList 的大小.
*/
private int size;

2.2 构造方法

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
41
42
43
44
45
46
47
48
49

/**
* 构造具有指定初始容量的空列表。
*
* @param initialCapacity 列表的初始容量
* @throws 如果容量为负数, 抛出 IllegalArgumentException
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 指定大小大于0 创建指定大小的列表
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 指定大小为 0, 指向同一个空列表, 可以减少内存的使用
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 指定大小小于 0, 抛出 IllegalArgumentException 异常.
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}

/**
* 无参构造
* 创建一个大小为 10 的列表.
*/
public ArrayList() {
// 当添加第一个元素时, 任何指向 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将被扩展为容量为 DEFAULT_CAPACITY(10)的列表.
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
* 按照集合迭代器返回元素的顺序, 构造一个包含指定集合元素的列表.
*
* @param c 要将其元素放入此列表的集合.
* @throws 如果参数为null, 抛出 NullPointerException
*/
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// 指向空数组.
elementData = EMPTY_ELEMENTDATA;
}
}

2.3 添加

2.3.1 添加单个元素

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

/**
* 将指定的元素追加到此列表的末尾。
*
* @param e 要添加的元素
* @return true (由 Collection.add 指定)
*/
public boolean add(E e) {
// 检查是否需要扩容
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}


/**
* 在列表的指定位置插入一个元素, 该位置之后的元素后移一位.
*
* @param index 要插入的位置的索引
* @param element 插入的元素
* @throws 索引超出边界,抛出 IndexOutOfBoundsException
*/
public void add(int index, E element) {
// 判断索引是否超出界限, 超出抛出 IndexOutOfBoundsException 异常.
rangeCheckForAdd(index);

ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是用空参数创建的列表, 第一次添加数据时将其扩展为容量最小为 DEFAULT_CAPACITY(10) 的列表.
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}


private void ensureExplicitCapacity(int minCapacity) {
// 用于记录列表被修改的次数.
// 用来限制用户在迭代时修改列表, 造成数据错乱.
modCount++;

// 扩容, 可能溢出
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}


/**
* 列表的最大容量.
* 有些虚拟机在数组中保存 header word 头部字, 用于存储数组的容量, 占八位.因此,列表的最大容量为 Integer.MAX_VALUE - 8
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
* 扩容, 确保至少可以容纳 minCapacity 个元素
*
* @param minCapacity 所需最小容量
*/
private void grow(int minCapacity) {
// 存在整数溢出的可能
int oldCapacity = elementData.length;
// 扩大 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 可能整数溢出
// 可能 newCapacity < minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 通过 System.arraycopy 方法将原数组拷贝到容量为 newCapacity 的新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

2.3.2 批量添加

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
41
42
43
44

/**
* 将指定集合中的所有元素按指定集合的迭代器返回的顺序追加到此列表的末尾
* 添加过程中的元素有发生改变的可能.
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}

/**
* 从指定位置开始, 将指定集合中的元素插入此列表. 该位置及其之后的元素后移.
*/
public boolean addAll(int index, Collection<? extends E> c) {
// 判断索引是否超出界限, 超出抛出 IndexOutOfBoundsException 异常.
rangeCheckForAdd(index);

Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);

int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);

System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}


/**
* 判断索引是否超出界限.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

2.4 删除

2.4.1 删除单个元素

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

/**
* 删除指定位置的元素.
* 其后元素前移.
*
* @param index 要删除元素的索引
* @return 被删除的元素
* @throws 超出边界,抛出 IndexOutOfBoundsException
*/
public E remove(int index) {
// 判断索引是否超界
rangeCheck(index);
// 列表修改次数加一
modCount++;
E oldValue = elementData(index);

int numMoved = size - index - 1;
if (numMoved > 0)
// 将elemenData中index+1及其后面的元素都向前移动一个下标
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 根据上一步的操作, size-1位置的对象向前移动了一个下标
// 如果没有elementData[--size]==null, 可能会导致内存泄漏, 若没有这一步操作, 该内存一直指向之前的元素, GC 不会认为它是垃圾, 故无法回收内存造成内存泄漏.
elementData[--size] = null;

return oldValue;
}


/**
* 检查索引是否在范围内.
* 和 添加元素 时的检查函数的区别为: 此函数不检查索引是否为负数, 并且索引不能为 size. 因为在执行此方法之前数组会检查索引, 如果为负数会抛出 ArrayIndexOutOfBoundsException.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
* 从列表中删除指定元素的第一个匹配, 如果列表中没有此元素, 则保持列表不变
* null 也被当作一个元素
* @param o 指定删除的元素
* @return true 如果删除了指定元素
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
// 删除该位置元素, 其后元素前移
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
// 删除该位置元素, 其后元素前移
fastRemove(index);
return true;
}
}
return false;
}

/*
* 删除当前位置的元素, 其后元素前移
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
// 删除的元素不是最后一个
if (numMoved > 0)
// 索引后的元素前移
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 同上, 让 GC 回收内存.
elementData[--size] = null;
}

2.4.2 批量操作

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

/**
* 删除列表中索引介于 [fromIndex, toIndex) 的元素.
* 索引为 fromIndex 的元素会被删除, 索引为 toIndex 的元素会被保留
* @throws 超界抛 IndexOutOfBoundsException
*/
protected void removeRange(int fromIndex, int toIndex) {
// 修改次数 + 1
modCount++;
// toIndex(包括)之后的元素数量
int numMoved = size - toIndex;
// 将 toIndex(包括)之后的所有元素复制到 fromIndex(包括)之后.
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);

// 删除后的元素数量
int newSize = size - (toIndex-fromIndex);
// GC!! 干活了
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}

/**
* 从此列表中删除指定集合中包含的所有元素
*
* @param c 指定的集合
* @return 如果在删除期间此 列表 因调用而更改, 返回 true
* @throws 如果集合中的元素和列表元素不兼容, 抛出 ClassCastException
* @throws 指定集合为null, 抛出 NullPointerException
*/
public boolean removeAll(Collection<?> c) {
// 非空检验
Objects.requireNonNull(c);
return batchRemove(c, false);
}

/**
* 从此列表中 保留 指定集合中包含的所有元素
* 和上个方法的区别为一个删除, 一个保留
* 具体差异: 传入 batchRemove 的参数不同
*/
public boolean retainAll(Collection<?> c) {
// 非空检验
Objects.requireNonNull(c);
return batchRemove(c, true);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
// 将需要保留的元素前移
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// 即使 c.contains() 抛出异常, 也要于AbstractCollection 保持行为兼容性
// 备注:ArrayList重写了AbstractCollection中的removeAll方法,removeAll调用了batchRemove
if (r != size) {
// 可能因 try 中的循环出现了异常
// 可能其他线程调用了该列表
// 将多出来的元素复制到 try 保留的元素后面.
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// GC 干活
for (int i = w; i < size; i++)
elementData[i] = null;
// 这里为什么不是 + 1?
// 若有读者知道原因, 还望在评论区告知.
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}


/**
* 清空列表中的元素.
* 执行完成后, 列表为空(全为null)
*/
public void clear() {
modCount++;

// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;

size = 0;
}

2.5 修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* 用指定元素替换列表中指定位置的元素.
*
* @param index 指定位置的索引
* @param element 存在指定位置的新元素
* @return 指定位置时旧元素
* @throws 索引超界 IndexOutOfBoundsException
*/
public E set(int index, E element) {
// 和 remove 用的同一个索引检查函数
rangeCheck(index);

// 更换新元素, 返回旧元素
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}

2.6 查找

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
   
// 位置访问操作
// 告诉编译器忽略 unchecked 警告信息
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}

/**
* 获取列表指定位置的元素.
*
* @param index 要获取的元素的索引
* @return 指定位置的元素
* @throws 索引超界, 抛出 IndexOutOfBoundsException
*/
public E get(int index) {
// 同 remove, set
rangeCheck(index);
// 这里应该是方便强制类型转换.
return elementData(index);
}


/**
* 返回指定元素第一次出现的索引
* 如果列表中没有此元素, 返回 -1
* contains 方法就是调用的此方法: return indexOf(o) >= 0;
*/
public int indexOf(Object o) {
if (o == null) {
// null 也当成一个元素
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}

/**
* 返回指定元素最后一次出现的索引
* 如果列表中没有此元素, 返回 -1
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}

2.7 序列化和反序列化

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
41
42
43
44
45
46
47
48
49
50
51

/**
* 序列化
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// fail-fast, 后续判断是否有并发处理
int expectedModCount = modCount;
// 序列化没有标记 static, transient的字段包括 size.
s.defaultWriteObject();

// 序列化 size
s.writeInt(size);

// 按序 序列化所有元素
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
// 有其他线程修改列表
if (modCount != expectedModCount) {
// 抛出 fail-fast 异常.
throw new ConcurrentModificationException();
}
}

/**
* 反序列化
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;

// 反序列化没有标记 static, transient的字段包括 size.
s.defaultReadObject();

// 反序列化 size
s.readInt();

if (size > 0) {
// 根据 size 扩容.
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);

Object[] a = elementData;
// 反序列化元素并加入列表中.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}

2.8 其他API

2.8.1 排序 (sort)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   // 重写于 List 接口
@Override
// 消除 unchecked 警告
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
// fail-fast, 后续判断是否有并发处理
final int expectedModCount = modCount;
// 排序, 底层使用 TimSort 实现, XXXX
Arrays.sort((E[]) elementData, 0, size, c);
// ArrayList被并发处理, 抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// 修改次数 + 1
modCount++;
}

2.8.2 转为数组 (toArray)

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

/**
* 按正确顺序包含此列表中所有元素的数组
* 返回的数组为 elementData 的复制, 修改返回的数组不会对 elementData 造成影响.
*
* @return 按正确顺序包含此列表中所有元素的数组
*/
public Object[] toArray() {
// 直接复制 elementData, 然后返回
return Arrays.copyOf(elementData, size);
}

/**
* 返回运行时指定数组类型的数组.
* 如果指定数组的容量小于 size, 新建一个容量为size的数组返回.
* 否则将数据复制到指定数组返回.
*
* @param a 指定数组, 如果 a 足够大(容量大于 size), 返回该数组, 否则返回一个相同类型的新数组.
*
* @return 包含列表元素的数组
* @throws 如果指定数组的类型不是列表元素类型的父类, 抛出 ArrayStoreException
* @throws 指定数组为null, 抛出 NullPointerException
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// 利用反射, 返回一个大小为size , 类型和 a 相同的新数组.
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 将数据复制到 a 中
System.arraycopy(elementData, 0, a, 0, size);
// 将 a[size] 置为 null(如果有的话), 对以后确定列表的长度很有用, 但只在调用方知道列表中不包含任何 null 元素时才有用.
if (a.length > size)
a[size] = null;
return a;
}

2.8.3 克隆 (clone)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/**
* 返回此 ArrayList 实例的副本, 浅拷贝
* 返回 Object 使用时需要强制转换
* @return 此 ArrayList 的副本
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
// 通过 Arrays.copyOf 拷贝数据.
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}

2.8.4 subList

1
2
3
4
5
6
7
8
9
10
11
12

/**
* 返回此列表 [fromIndex,toIndex) 之间的数据的 视图(SubList 类).
* 如果 formIndex 和 toIndex 相等, 返回列表为空.
* 返回的列表中的 elementData 和此列表的 elementData 是同一个.
* 通过传递子视图可以将列表的操作用作范围操作, 比如: 删除一部分元素可以用, list.subList(from, to).clear();
*
*/
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}

2.8.5 遍历方式

ArrayList 可以通过 for, foreach, foreach-lambda, iterator进行遍历.

iterator 又可分为 Iterator 和 ListIterator, 他们分别由内部类 Itr 和 ListItr 实现.

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
41
42
43
44
45
46
47
48
49
50
51
List<String> strList = new ArrayList<String>(4);
strList.add("1");
strList.add("2");
strList.add("3");

// for遍历
for (int i = 0; i < strList.size(); i++) {
System.out.println(strList.get(i));
}

// foreach
// 备注: 只要实现Iterable接口, 就能使用foreach
for (String s : strList) {
System.out.println(s);
}

// foreach-lambda遍历
strList.forEach(System.out::println);

// iterator遍历
Iterator<String> iterator = strList.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
// 下一次出现ConcurrentModificationException
// 问题是因为list的iterator方法返回的是ArrayList的内部类Itr
// Itr里面的expectedModCount会与ArrayList的modCount进行比较
// 剩下的就不言而喻
strList.remove(str);
// 进行iterator遍历时, 如果进行remove等操作, 调用iterator的方法
// 而不是ArrayList的方法
// iterator.remove();
}

// ListIterator可以进行顺序, 逆序遍历, 可以指定index位置开始遍历
ListIterator<String> iterator = strList.listIterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
iterator = strList.listIterator(strList.size());
while (iterator.hasPrevious()){
System.out.println(iterator.previous());
iterator.remove();
}

// 使用并行遍历, 可以将元素放在不同的迭代器进行并行遍历
Spliterator<String> spliterator = strList.spliterator();
// split, 分而治之, 类似算法里面的分治
Spliterator<String> otherSpliterator = spliterator.trySplit();
spliterator.forEachRemaining(System.out::println);
otherSpliterator.forEachRemaining(System.out::println);

3. 一些问题

3.1 elementData 为什么用 transient 修饰?

transient 用来表示一个域不是该对象序列化的一部分, 当一个对象被序列化的时候, transient 修饰的变量的值是不包括在序列化的表示中的.

但是 ArrayList 又是可序列化的类, elementData 是 ArrayList 具体存放元素的成员, 用 transient 修饰岂不是无法正确序列化, 反序列化了?

其实ArrayList 是通过 writeObject 和 readObject 实现序列化和反序列化.

ArrayList 在序列化的时候会调用 writeObject, 将 size 和 elementData 写入 java.io.ObjectOutputStream; 反序列化时再调用 readObject 从 java.io.ObjectOutputStream 获取 size 和 element, 再恢复到 elementData.

为什么不直接对 elementData 序列化呢?

我们知道 elementData 的大小是动态变化的, 绝大多数时候 elementData 的大小要比它存的数据要多, 也就是说 elementData 里面有空数据, 序列化时我们当然不想将这些空数据序列化, 因此 ArrayList 采用了上述的方法序列化, 而不是直接序列化 elementData.

3.2 elementData 为什么是 Object 而不是泛型 E?

  1. Java 中泛型的运用的目的是实现对象的重用, 泛型 T 和 Object 类在编写时没有太区别, 只是 JVM 中没有 T 的概念, T 只是存在于编程写时, 进入 JVM 运行时, JVM 会对泛型进行擦除, 也就是替换 T 为第一个限定的类型, 如果没有限定类型就会替换为 Object.
  2. Object 可以实例化, 泛型不能实例化.
  3. 从反射方面来说, 返回一个泛型的实例时, 不需要经过强制转换, Object 则需要强制转换.

3.3 modCount 的作用

modCount 继承于父类 AbstractList, 源码如下:

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
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass
* wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and
* {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to
* {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw
* bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be
* ignored.
*/
protected transient int modCount = 0;

modCount 是记录此列表结构修改的次数. 结构修改是指那些改变列表大小的修改, 或者以某种方式扰乱列表, 使得正在进行的迭代可能产生不正确的结果. 此字段由迭代器和 listiterator 方法返回的迭代器和列表迭代器实现使用. 如果此字段的值意外更改, 迭代器会抛出 ConcurrentModificationException 响应next, remove, previous, set 或者 add操作. 提供了快速失败的行为, 而不是在迭代过程中面对并发修改的不确定性行为.

原理:

迭代器将此刻的 modCount 记录下来, 迭代器进行相关操作时查看现在的 modCount 和记录下来的值是否一致, 如果不一致说明发生了并发修改, 抛出异常.

具体实现:

调用 list.iterator() 时返回一个 ArrayList 的内部类 Itr.

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}

/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

Itr() {}

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}

public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

expectedModCount 的初始值为 modCount (记录当前的修改次数)

hasNext的判断条件为: cursor!=size 当前迭代的位置不是最大容量返回 true.

next 和 remove 操作之前都会检查 expectedModCount 和 modCount 是否相等, 不相等会抛出 ConcurrentModificationException.

如果没有这步操作, 可能会抛出 ArrayIndexOutOfBoundsException 异常(迭代时列表被并发删除了部分数据).

3.4 为什么 hugeCapacity() 会返回 Integer.MAX_VALUE?

MAX_ARRAY_SIZE 不是 Integer.MAX_VALUE - 8 吗, 为什么 hugeCapacity() 还返回 Integer.MAX_VALUE?

前文提到: MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 是因为部分虚拟机在数组中保存 header word 头部字占用 8 个空间. 在这些虚拟机中分配大于IMAX_ARRAY_SIZE 的空间会导致 OOM, 而在其他的虚拟机中则可以正常扩容.

而 gorw 方法要确保至少可以容纳 minCapacity 个元素. 又因为 newCapacity > MAX_ARRAY_SIZE, 因此只能将列表扩容到 Integer.MAX_VALUE.

3.5 retainAll 和 removeall中 ,modCount 为什么加 size - w

其他结构性修改操作中, modCount 的值都是 + 1, 为什么在 retainAll 和 removeAll 中加的却是 size - w?

因为 modCount 只是记录结构修改的次数 ,加 1 和加 size - w 区别不大?

如果有大佬知道原因还望告知.

评论




博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

载入天数...载入时分秒... 本站使用 Volantis 作为主题 鲁ICP备-20012065号