在 Java 集合框架中,ArrayList
、LinkedList
和 Vector
是最常用的 List 实现类 。它们都实现了 List
接口,可以存储有序、可重复的元素,但在底层实现、线程安全性以及使用场景方面存在明显差异。本文将系统对比三者的实现与区别。
一、三者的共同点
-
都实现了 List 接口,元素有序、可重复。
-
都可以根据索引访问元素,支持随机访问。
-
都可以存储
null
元素。
二、ArrayList
1. 底层实现
-
ArrayList
基于 动态数组(Object[]) 实现。 -
默认容量为 10,当元素数量超过容量时,会触发扩容。
-
扩容规则:原容量的 1.5 倍。
// JDK1.8 ArrayList 扩容源码
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为1.5倍
2. 特点
-
优点:
-
随机访问快(基于数组,时间复杂度 O(1))。
-
内存连续,缓存友好。
-
-
缺点:
-
插入/删除慢(需要移动元素,O(n))。
-
扩容需要拷贝数组,性能损耗大。
-
3. 适用场景
- 读多写少的场景,例如查询列表、按索引访问元素。
三、LinkedList
1. 底层实现
-
LinkedList
基于 双向链表 实现。 -
每个节点是一个
Node
对象,包含prev
、next
和item
三个字段。
// JDK1.8 LinkedList 节点定义
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
2. 特点
-
优点:
-
插入/删除快(只需修改前后指针,O(1))。
-
适合频繁插入和删除的场景。
-
-
缺点:
-
随机访问慢(需要从头/尾遍历,O(n))。
-
额外的指针存储,空间开销大。
-
3. 适用场景
- 写多读少的场景,例如队列、双端队列、频繁插入删除的数据结构。
四、Vector
1. 底层实现
-
Vector
与ArrayList
类似,也是基于 动态数组 实现。 -
默认容量为 10,扩容规则为:
-
如果指定了
capacityIncrement
,则扩容时按capacityIncrement
增加。 -
否则扩容为 原容量的 2 倍。
-
// JDK1.8 Vector 扩容源码
int newCapacity = oldCapacity * 2;
2. 特点
-
优点:
- 线程安全(大部分方法使用
synchronized
修饰)。
- 线程安全(大部分方法使用
-
缺点:
-
因为加锁,性能比 ArrayList 慢。
-
已经逐渐被淘汰,推荐使用
ArrayList + Collections.synchronizedList()
或CopyOnWriteArrayList
。
-
3. 适用场景
- 在 多线程环境 下需要保证线程安全时(但更推荐使用并发包中的
CopyOnWriteArrayList
)。
五、三者的对比
特性 | ArrayList | LinkedList | Vector |
---|---|---|---|
底层结构 | 动态数组 | 双向链表 | 动态数组 |
随机访问 | O(1) | O(n) | O(1) |
插入/删除 | O(n)(移动元素) | O(1)(改指针) | O(n) |
是否线程安全 | ❌ 否 | ❌ 否 | ✅ 是(synchronized) |
扩容机制 | 1.5 倍 | - | 2 倍(或自定义) |
空间开销 | 小 | 大(指针开销) | 小 |
推荐场景 | 读多写少 | 写多读少 | 多线程但过时 |
六、使用建议
-
单线程环境 :优先使用
ArrayList
(性能最佳)。 -
频繁插入/删除 :选择
LinkedList
。 -
多线程环境 :避免使用
Vector
,更推荐:-
Collections.synchronizedList(new ArrayList<>())
-
CopyOnWriteArrayList
-
七、总结
-
ArrayList:基于动态数组,查询快,插入删除慢,适合读多写少。
-
LinkedList:基于双向链表,插入删除快,查询慢,适合写多读少。
-
Vector:基于动态数组,线程安全,但性能差,已被替代。
👉 在实际开发中,ArrayList
是最常用的,LinkedList
适合做队列/栈,Vector
基本淘汰,推荐使用并发容器。