JAVA 中的 ArrayList 工作原理
ArrayList 的核心工作原理
ArrayList
是 Java 中基于 动态数组 实现的高效列表,核心机制如下:
1. 底层数据结构
- 动态数组:内部使用
Object[] elementData
存储元素,支持动态扩容。 - 内存连续:元素在内存中连续分配,随机访问性能高(时间复杂度 O(1))。
2. 自动扩容机制
- 默认初始容量:10(空构造方法初始化时数组为空,首次添加元素时扩容到 10)。
- 触发条件:当添加元素时,若当前容量不足(
size >= elementData.length
),触发扩容。 - 扩容策略:
若仍不足,则直接扩容到所需的最小容量(如// 新容量 = 旧容量 + 旧容量右移1位(即旧容量的1.5倍) int newCapacity = oldCapacity + (oldCapacity >> 1);
addAll(Collection)
时)。
3. 增删改查操作
- 添加元素:
- 尾部追加(
add(E e)
):直接插入,时间复杂度 O(1)。 - 指定位置插入(
add(int index, E e)
):需移动后续元素,时间复杂度 O(n)。
- 尾部追加(
- 删除元素:
- 删除末尾元素(
remove(int index)
):时间复杂度 O(1)。 - 删除中间元素:需移动后续元素,时间复杂度 O(n)。
- 删除末尾元素(
- 查询元素:通过索引直接访问(
get(int index)
),时间复杂度 O(1)。
4. 线程安全性
- 非线程安全:默认未加锁,多线程并发修改可能导致数据不一致或 ConcurrentModificationException。
- 同步方案:
或使用List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
CopyOnWriteArrayList
(读多写少场景)。
5. 快速失败机制(Fail-Fast)
- 迭代器行为:迭代过程中若检测到结构性修改(如增删元素),立即抛出
ConcurrentModificationException
。 - 实现原理:通过
modCount
字段记录修改次数,迭代时检查是否与预期一致。
6. 性能优化建议
- 预分配容量:若已知数据量,初始化时指定容量避免频繁扩容。
ArrayList<String> list = new ArrayList<>(1000); // 初始容量1000
- 避免频繁中间插入/删除:大量中间操作时,优先考虑
LinkedList
。
核心源码解析
// 添加元素(触发扩容)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 检查容量
elementData[size++] = e; // 尾部插入
return true;
}
// 扩容逻辑
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
if (newCapacity < minCapacity) newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
对比其他集合
维度 | ArrayList | LinkedList | Vector |
---|---|---|---|
数据结构 | 动态数组 | 双向链表 | 动态数组(线程安全) |
随机访问性能 | O(1) | O(n) | O(1) |
中间插入/删除 | O(n) | O(1) | O(n) |
线程安全 | 否 | 否 | 是(同步锁) |
总结
- 适用场景:读多写少、需要频繁随机访问、内存占用敏感的场景(如缓存列表)。
- 避坑指南:避免多线程直接操作,预估容量减少扩容开销,优先用
Iterator
遍历。 - 扩展思考:在 Java 8+ 中,
ArrayList
的spliterator()
支持并行流处理(需注意线程安全)。