核心结论先行
一句话概括:Vector 是一个古老的、线程安全的类,而 List 是一个接口,它定义了列表的行为,ArrayList 和 LinkedList 是 List 接口最常用的两个实现,在现代 Java 编程中,除非有非常特殊的并发需求,否则应该优先使用 ArrayList 而不是 Vector。
从概念和层次结构上理解
要明白它们在 Java 集合框架中的位置和角色是完全不同的。
List (接口)
- 是什么:
List是一个接口(Interface),位于java.util.List。 - 作用:它定义了一个有序的、可以包含重复元素的集合的规范或契约,任何实现了
List接口的类,都必须提供add(),get(),remove(),size()等方法。 - 实现类:
ArrayList,LinkedList,Vector,Stack等都是List接口的实现类。 - 类比:你可以把
List理解成“列表”这个概念本身,它描述了列表应该具备哪些功能。

(图片来源: Oracle Java Docs)
Vector (类)
- 是什么:
Vector是一个具体的类(Concrete Class),位于java.util.Vector。 - 作用:它是一个实现了
List接口的类,并且从 JDK 1.0 就存在了,它提供了一种动态数组的功能。 - 关键特性:
Vector最大的特点是线程安全(Thread-Safe)。 - 类比:
Vector是“列表”这个概念的一个具体实现,而且是一个自带“安全锁”的实现。
详细对比表格
| 特性 | Vector |
List (接口) |
ArrayList (最常用实现) |
|---|---|---|---|
| 类型 | 具体类 | 接口 | 具体类 |
| 线程安全 | 是,所有公共方法都使用 synchronized 关键字,是同步的。 |
不保证。List 接口本身没有定义任何同步机制。 |
否,非同步,不是线程安全的。 |
| 性能 | 较低,因为每个方法都有同步开销(锁竞争),在单线程或多线程环境下性能都不如 ArrayList。 |
- | 高,在非并发场景下,没有同步开销,性能最好。 |
| 扩容机制 | 容量翻倍,当内部数组满了,新数组的大小是原来的 2 倍。 | - | 容量大约增加 50%,当内部数组满了,新数组的大小是原来的 5 倍,这种策略通常能更好地利用内存。 |
| 历史/遗留 | 遗留类,从 JDK 1.0 存在,是 Java Collections Framework (JCF) 出现之前的产物。 | 核心接口,JCF 的核心组成部分,自 JDK 1.2 引入。 | 现代首选,JCF 引入后,作为 Vector 的非线程安全替代品。 |
| 使用场景 | 极少,在需要强一致性且对性能要求不高的旧代码中可能见到,现代开发几乎被 Collections.synchronizedList() 或 CopyOnWriteArrayList 替代。 |
声明变量,当你只关心列表功能,不关心具体实现时,应使用 List list = new ArrayList<>();。 |
通用场景,绝大多数情况下,当你需要一个列表时,都应首选 ArrayList。 |
| 遍历方式 | 支持 Iterator,也支持遗留的 Enumeration。 |
支持 Iterator, for-each 循环等。 |
支持 Iterator, for-each 循环等。 |
关键差异深入解析
1 线程安全 vs. 性能
这是两者最核心的区别。
-
Vector的线程安全实现:// Vector 源码中的 add 方法 (简化版) public synchronized E add(E e) { ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return e; }synchronized关键字确保了在同一时间只有一个线程可以执行add方法,这在多线程环境下可以防止数据不一致,但也带来了巨大的性能代价,即使是在单线程环境下,这个锁也是完全多余的。 -
ArrayList的高性能:// ArrayList 源码中的 add 方法 (简化版) public boolean add(E e) { ensureCapacityInternal(size + 1); // 没有同步 elementData[size++] = e; return true; }由于没有同步开销,
ArrayList的操作速度远快于Vector。
2 如何在需要线程安全时选择?
既然 Vector 性能不好,那我们如何在需要线程安全的列表时做选择呢?
使用 Collections.synchronizedList() 包装器
这是最常见的方式,它将一个非线程安全的列表(如 ArrayList)包装成一个线程安全的列表。
List<String> list = Collections.synchronizedList(new ArrayList<>());
// 遍历时需要手动同步
synchronized (list) {
for (String item : list) {
// do something
}
}
这种方式的好处是:
- 灵活性:你可以选择任意
List实现(如ArrayList)作为基础。 - 性能更好:只有在修改列表(增删改)时才进行同步,遍历时由用户手动同步,比
Vector的“处处同步”更高效。
使用 CopyOnWriteArrayList
这是一个专门为并发遍历优化的列表,它的特点是写时复制,当修改列表时,它会复制一份底层数组,在副本上进行修改,然后替换引用,这使得读操作完全无锁,性能极高。
List<String> threadSafeList = new CopyOnWriteArrayList<>();
适用场景:读多写少的场景,一个事件监听器列表,大部分时间只是读取监听器,偶尔会添加或移除。
继续使用 Vector (不推荐)
除非你在维护一个非常古老的系统,Vector 的使用模式已经深入其中,否则没有理由再主动选择它。
代码示例
1 现代 Java 推荐用法
import java.util.ArrayList;
import java.util.List;
public class ModernListExample {
public static void main(String[] args) {
// 1. 声明为接口,实现为 ArrayList (最佳实践)
// 这意味着你未来可以轻松切换为 LinkedList,而无需修改调用处的代码。
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
System.out.println("List: " + names);
System.out.println("First element: " + names.get(0));
// 2. 如果需要线程安全,使用包装器
List<Integer> numbers = Collections.synchronizedList(new ArrayList<>());
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 注意:同步的 List 在迭代时也需要手动同步
synchronized (numbers) {
for (Integer num : numbers) {
System.out.println(num);
}
}
}
}
2 不推荐的用法 (过时的 Vector)
import java.util.Vector;
public class LegacyVectorExample {
public static void main(String[] args) {
// 3. 除非有特殊理由,否则不要使用 Vector
Vector<String> legacyVector = new Vector<>();
legacyVector.add("One");
legacyVector.add("Two");
legacyVector.add("Three");
System.out.println("Vector: " + legacyVector);
System.out.println("First element: " + legacyVector.get(0));
}
}
总结与建议
List (接口) |
Vector (类) |
ArrayList (类) |
|
|---|---|---|---|
| 角色 | 规范 | 过时的、线程安全的实现 | 现代、高性能的通用实现 |
| 线程安全 | 不保证 | 是 (性能差) | 否 |
| 何时用 | 总是用来声明变量,表示这是一个列表。 | 几乎不用,维护旧代码时可能遇到。 | 几乎总是用,99% 的列表需求都应该用它。 |
| 替代方案 | - | Collections.synchronizedList() |
- |
最终建议:
- 永远优先使用
List接口来声明你的变量,List<String> myList = ...,这体现了面向接口编程的思想,提高了代码的灵活性和可维护性。 - 在实现列表时,首选
ArrayList,它提供了在大多数情况下最优的性能。 - 只有在明确需要并发安全时,才考虑使用
Collections.synchronizedList()或CopyOnWriteArrayList,而不是Vector。 - 将
Vector视为一个历史遗留物,了解它即可,但在新代码中尽量避免使用。
