Vector 的 add() 和 get() 比 ArrayList 慢一倍以上,因其所有公共方法默认加 synchronized 粗粒度锁,单线程下也无法优化;ArrayList 无锁,性能更高。

Vector 的 add() 和 get() 为什么比 ArrayList 慢一倍以上
因为 Vector 的每个公共方法(比如 add()、get()、size())都默认加了 synchronized,哪怕你只是单线程读取。JVM 无法对这种粗粒度锁做有效优化,每次调用都要进 monitor,而 ArrayList 完全无锁。
实操建议:
- 单线程场景下,直接换
ArrayList—— 不仅快,API 完全兼容 - 如果历史代码里有
Vector被多个线程读写,别急着改锁,先确认是否真存在并发修改:多数“多线程 Vector”其实只是被多个线程读、极少写,这时用Collections.synchronizedList(new ArrayList())反而更灵活(可手动控制同步块) -
Vector的扩容机制和ArrayList类似(默认翻倍),但同步开销掩盖了这点差异,实际扩容成本反而更高
ConcurrentHashMap 替代不了 Vector,但 CopyOnWriteArrayList 可能误用
Vector 是动态数组 + 全局锁,语义上接近“线程安全的数组列表”。但很多人想当然用 CopyOnWriteArrayList 替代,结果在频繁写场景下 OOM 或卡顿——因为它每次 add() 都复制整个数组。
常见错误现象:
立即学习“Java免费学习笔记(深入)”;
- 日志收集器用
CopyOnWriteArrayList存每条请求日志,QPS 上千后 GC 飙升 - 误以为
ConcurrentHashMap“更现代”,就把它当Vector用(比如存有序 ID 列表),结果发现不能按索引取值、不保序
使用场景判断:
- 读多写少(如配置监听器列表)→
CopyOnWriteArrayList - 需要分段并发写 + 高频遍历 →
ConcurrentLinkedQueue或手动分片ArrayList - 真要保留顺序且需线程安全随机访问 → 得自己封装,比如用
ReentrantLock+ArrayList,只锁必要操作
遗留系统里怎么安全替换 Vector(不改接口也不崩)
很多老项目把 Vector 当返回类型暴露在 public API 里,比如方法签名是 public Vector<string> getConfigs()</string>。直接改成 ArrayList 会破坏二进制兼容性。
实操建议:
- 保持返回类型仍是
Vector,但内部用ArrayList实现,构造时 newVector(new ArrayList())——Vector的构造函数支持 Collection,且后续所有操作走子类逻辑 - 如果调用方确实会调用
Vector特有方法(比如elementAt()),那就不能动,只能加监控:用 JVM TI 或字节码工具统计Vector方法调用频次,确认哪些方法实际没人用 - 注意
Vector的capacity()和ensureCapacity()是公开 API,如果外部依赖这些,替换后行为可能变化(ArrayList没对应方法)
为什么 Collections.synchronizedList(new ArrayList()) 不是万能解
它只是给 ArrayList 包了一层 synchronized 包装器,所有方法调用都串行化,性能比原生 Vector 略好(少了继承层级开销),但本质还是同一把锁。
容易踩的坑:
- 迭代时没手动同步:写
for (String s : list)会抛ConcurrentModificationException,必须写成synchronized(list) { for (...) } - 复合操作非原子:比如
if (!list.contains(x)) list.add(x),即使用了synchronizedList,中间仍可能被其他线程插入相同元素 - 它不解决扩容竞争问题:高并发 add 下,多个线程可能同时触发扩容,造成冗余数组分配
真正复杂的并发列表逻辑,往往得跳出“集合类”思维,用 BlockingQueue 流式处理,或用 StampedLock 控制读写分离 —— 这才是 Vector 被淘汰后,留下的真实复杂点。



















