现代业务开发中,除非维护旧系统,否则不建议直接使用 Vector。单线程场景首选 ArrayList,多线程场景建议使用 Collections.synchronizedList 或并发包下的集合类。
先说结论:Vector 属于遗留类,性能损耗大,新开发场景应优先选择 ArrayList 配合并发工具类。
- 适合:单线程高频读写场景使用 ArrayList,多线程共享数据场景使用并发集合。
- 重点看:Vector 方法大多被 synchronized 修饰,并发下性能显著低于 ArrayList。
- 别忽略:ArrayList 非线程安全,多线程下直接操作可能导致数据覆盖或抛出异常;使用 Collections.synchronizedList 遍历时需手动加锁。
快速决策与代码替换
遇到集合选型问题时,按以下逻辑快速决策并实施代码替换:
- 确认并发场景:检查当前模块是否涉及多线程并发读写。
- 单线程场景:直接选用 ArrayList 以获得最佳性能。
- 多线程场景:避免直接使用 Vector,改用 Collections.synchronizedList 包装 ArrayList 或使用 CopyOnWriteArrayList。
代码替换示例:
// 不推荐:Vector 遗留类
List<String> vector = new Vector<>();
// 推荐:单线程场景
List<String> list = new ArrayList<>();
// 推荐:多线程场景(通用)
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 推荐:多线程场景(读多写少)
List<String> cowList = new CopyOnWriteArrayList<>();核心原理与性能差异
Vector 和 ArrayList 底层都基于动态数组实现,但设计年代和线程安全机制不同。Vector 是 JDK 1.0 的遗留类,其方法几乎都加了 synchronized 关键字,导致每次操作都需要获取锁,在高并发下会成为性能瓶颈。相比之下,ArrayList 作为 JDK 1.2 集合框架的一部分,设计更符合现代 Java 开发习惯,无锁开销。
此外,两者扩容机制也有差异。Vector 默认扩容为当前容量的 2 倍,而 ArrayList 扩容为原容量的 1.5 倍。频繁的扩容操作也会影响性能,建议初始化时指定合理容量。
代码实战:线程安全验证
以下代码展示了 ArrayList 在多线程下的风险,以及 Collections.synchronizedList 的正确用法。
1. ArrayList 线程不安全示例(可能抛出 ConcurrentModificationException):
List<String> list = new ArrayList<>();
// 多线程同时 add 操作,可能导致数据丢失或异常
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> list.add("data"));
}
executor.shutdown();2. Collections.synchronizedList 正确遍历示例(必须手动同步):
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 遍历必须放在 synchronized 块中,防止 ConcurrentModificationException
synchronized (syncList) {
for (String s : syncList) {
System.out.println(s);
}
}3. CopyOnWriteArrayList 适用场景(读多写少):
// 适合监听器列表等读多写少场景,遍历时无需加锁
List<String> cowList = new CopyOnWriteArrayList<>();
for (String s : cowList) {
// 安全遍历,基于快照
System.out.println(s);
}验证与排查
替换完成后,可通过以下方式验证:
- 单元测试:编写多线程并发测试用例,确认没有出现数据丢失或 ConcurrentModificationException 异常。
- 性能观察:在高频调用接口观察响应时间,替换为 ArrayList 后理论上应有明显改善(单线程场景)。
- 日志检查:查看应用日志,确认没有因集合操作导致的报错。
- 代码扫描:使用 IDE 插件或静态扫描工具检查是否存在未同步的集合遍历操作。
常见坑与注意事项
- 盲目追求安全:不分场景滥用 Vector,导致单线程场景性能无故下降。
- 忽视同步:在多线程环境下直接使用 ArrayList,导致数据覆盖或统计结果不准确。
- 迭代器风险:使用 Collections.synchronizedList 时,迭代操作必须手动 synchronized 包裹,否则仍可能抛出 ConcurrentModificationException。
- 扩容开销:高频添加元素时,建议初始化指定容量,减少数组复制开销。
参考文档
- Oracle Java SE Documentation: java.util.Vector
- Oracle Java SE Documentation: java.util.ArrayList
- Oracle Java SE Documentation: java.util.Collections.synchronizedList
- Java Concurrency in Practice (Brian Goetz)