核心概念
ArrayList 和 LinkedList 都是 Java 集合框架中 List 接口的实现类,但它们的底层数据结构完全不同:
- ArrayList:基于动态数组实现
- LinkedList:基于双向链表实现
这两种不同的数据结构导致它们在性能和使用场景上有明显差异。
底层实现原理
ArrayList 的实现
public class ArrayList<E> extends AbstractList<E> {
// 底层是 Object 数组
transient Object[] elementData;
// 实际元素个数
private int size;
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
}
特点:
- 基于数组,元素在内存中连续存储
- 支持快速随机访问(通过索引)
- 插入/删除需要移动后续元素
LinkedList 的实现
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E> {
// 双向链表的头节点和尾节点
transient Node<E> first;
transient Node<E> last;
// 节点结构
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
}
特点:
- 基于双向链表,元素在内存中不连续
- 不支持快速随机访问
- 插入/删除只需修改指针
核心区别对比
| 对比维度 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 随机访问 | O(1),支持快速随机访问 | O(n),需要遍历 |
| 插入/删除(头部) | O(n),需要移动元素 | O(1),修改指针即可 |
| 插入/删除(尾部) | O(1),摊销复杂度 | O(1) |
| 插入/删除(中间) | O(n),需要移动元素 | O(n),需要先定位 |
| 内存占用 | 较小,只存储元素 | 较大,需存储前后指针 |
| 适用场景 | 频繁查询、遍历 | 频繁插入、删除 |
性能优化与使用场景
ArrayList 适用场景
// 1. 频繁随机访问
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add("item" + i);
}
// 随机访问非常快
String item = list.get(500); // O(1)
// 2. 遍历操作
for (String s : list) {
System.out.println(s);
}
// 3. 预知大小时,指定容量避免扩容
List<Integer> numbers = new ArrayList<>(1000);
LinkedList 适用场景
// 1. 频繁在头部/尾部插入删除
LinkedList<String> deque = new LinkedList<>();
deque.addFirst("first"); // O(1)
deque.addLast("last"); // O(1)
deque.removeFirst(); // O(1)
// 2. 实现队列或栈
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.poll();
Deque<Integer> stack = new LinkedList<>();
stack.push(1);
stack.pop();
// 3. 不确定数据量且需要频繁增删
List<String> list = new LinkedList<>();
list.add(0, "insert at head"); // 比 ArrayList 快
线程安全性考量
两者都不是线程安全的,多线程环境需要:
// 方案1:使用同步包装器
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 方案2:使用并发集合
List<String> concurrentList = new CopyOnWriteArrayList<>();
// 方案3:手动同步
synchronized(list) {
list.add("item");
}
面试答题总结
简洁版答案:
- 底层结构不同:ArrayList 基于动态数组,LinkedList 基于双向链表
- 性能差异:
- ArrayList 支持快速随机访问(O(1)),但插入/删除慢(O(n))
- LinkedList 头尾插入/删除快(O(1)),但随机访问慢(O(n))
- 内存占用:LinkedList 额外存储前后指针,内存占用更大
- 使用场景:
- ArrayList:查询多、插入删除少
- LinkedList:插入删除多、查询少,或用作队列/栈
加分项:
- ArrayList 扩容机制:默认初始容量10,扩容为1.5倍
- LinkedList 实现了 Deque 接口,可作为双端队列使用
- 实际开发中 ArrayList 使用更频繁,因为现代 CPU 缓存对连续内存访问更友好
- 即使频繁插入删除,ArrayList 在数据量不大时也可能比 LinkedList 快(缓存局部性)