ArrayList 源码深度解析(基于 JDK 8)
ArrayList 是 Java 集合框架中最常用的 List 实现类,基于动态数组实现,支持自动扩容,适用于频繁查询、少量增删的场景。本文将从继承关系、核心源码、扩容机制、迭代器实现等方面,全面解析 ArrayList 的工作原理。
ArrayList 核心特性与继承关系
核心特性
- 有序性:元素按插入顺序存储,支持通过索引访问(0 基索引)。
- 可重复性:允许存储重复元素,也允许存储
null值。 - 动态扩容:底层数组容量不足时自动扩容,无需手动管理大小。
- 非线程安全:多线程环境下并发修改可能导致数据不一致(需手动同步或使用
CopyOnWriteArrayList)。
继承关系

1 | public class ArrayList<E> extends AbstractList<E> |
- 继承
AbstractList:复用了 List 接口的部分默认实现(如addAll、indexOf等)。 - 实现
List:遵循 List 接口规范,支持列表的基本操作。 - 实现
RandomAccess:标记接口,表明支持快速随机访问(通过索引直接访问,时间复杂度O(1)),遍历此类集合时,普通 for 循环效率高于迭代器。 - 实现
Cloneable:支持克隆(浅拷贝)。 - 实现
Serializable:支持序列化,可通过流传输。
核心变量解析
ArrayList 的核心功能依赖于以下变量,决定了其存储方式和扩容行为:
1 | // 1. 默认初始容量(无参构造器第一次添加元素时的初始容量) |
关键区别:
EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是空数组,但前者用于用户指定容量为 0 的场景(如new ArrayList(0)),第一次添加元素时直接按需求扩容;后者用于无参构造器,第一次添加元素时会扩容至DEFAULT_CAPACITY(10)。
构造函数解析
ArrayList 提供三种构造函数,用于初始化底层数组:
自定义初始容量
1 | public ArrayList(int initialCapacity) { |
无参构造器(最常用)
1 | public ArrayList() { |
- 延迟初始化:无参构造器不会立即分配容量为 10 的数组,而是在第一次添加元素时才扩容,减少内存浪费。
通过集合初始化
1 | public ArrayList(Collection<? extends E> c) { |
核心方法解析
1. 添加元素:add(E e)
添加元素的核心是确保数组容量充足,不足则触发扩容。
1 | public boolean add(E e) { |
扩容流程详解:
计算所需最小容量:
1
2
3
4
5
6
7
8
9
10
11private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 若为无参构造的初始状态(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),则最小容量为 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity; // 否则直接返回所需容量
}检查是否需要扩容:
1
2
3
4
5
6
7private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 记录结构修改次数(用于迭代器的 fail-fast 机制)
// 若所需容量 > 当前数组长度,触发扩容
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}执行扩容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 扩容 1.5 倍(oldCapacity + oldCapacity/2,位运算更高效)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 若扩容后仍小于所需容量(如初始为空数组时),直接用所需容量
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 若超过最大容量,使用 Integer.MAX_VALUE 或 MAX_ARRAY_SIZE
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
// 复制原数组元素到新数组(扩容核心:创建新数组 + 复制元素)
elementData = Arrays.copyOf(elementData, newCapacity);
}扩容逻辑总结:
- 默认扩容 1.5 倍(平衡内存与性能);
- 特殊情况(如初始为空数组)直接按所需容量扩容;
- 最大容量限制为
Integer.MAX_VALUE(避免 OOM)。
2. 删除元素:remove(int index)
删除指定索引元素,核心是数组元素移位和帮助 GC。
1 | public E remove(int index) { |
性能特点:
- 中间位置删除需移动大量元素,时间复杂度
O(n),效率较低; - 尾部删除无需移动元素,效率高(
O(1))。
3. 获取元素:get(int index)
随机访问元素,体现 ArrayList 快速查询的优势。
1 | public E get(int index) { |
迭代器与 Fail-Fast 机制
ArrayList 的迭代器(Itr)是快速失败(Fail-Fast) 迭代器,用于检测并发修改,避免数据不一致。
迭代器核心实现
1 | private class Itr implements Iterator<E> { |
Fail-Fast 机制原理
- 触发条件:迭代过程中,若其他线程(或当前线程)通过非迭代器方法(如
list.remove(index))修改集合结构(modCount变化),则modCount != expectedModCount,迭代器立即抛出ConcurrentModificationException。 - 目的:及时发现并发修改,避免迭代器返回不一致的数据(快速失败而非默默出错)。
- 局限性:仅为调试辅助机制,不能保证 100% 检测到并发修改(如修改后又改回原值)。
使用注意事项
1. 初始化时指定容量
无参构造器默认第一次扩容至 10,若已知元素数量(如 1000 个),建议初始化时指定容量:
1 | List<String> list = new ArrayList<>(1000); // 避免多次扩容,提升性能 |
2. 遍历方式选择
随机访问场景:用普通 for 循环(效率高于迭代器):
1
2
3for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}需删除元素:必须用迭代器的remove()方法(避免ConcurrentModificationException):
1
2
3
4
5
6
7Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("a")) {
it.remove(); // 安全删除
}
}
3. 线程安全问题
ArrayList 非线程安全,多线程并发修改可能导致:
- 数据丢失(如同时 add 元素);
- 数组越界(扩容时未加锁,导致一个线程访问到无效索引)。
解决方案:
- 手动同步(
synchronized或Collections.synchronizedList); - 使用并发容器(如
CopyOnWriteArrayList,适合读多写少场景)