copyonwritearraylist能避免遍历中删除异常,因其采用写时复制机制:修改时创建新数组并原子更新volatile引用,迭代器持有快照副本,与写操作隔离;遍历时必须用iterator.remove()而非list.remove()。

在多线程环境下,对普通 ArrayList 边遍历边删除会触发 ConcurrentModificationException。而 CopyOnWriteArrayList 通过“写时复制”机制天然支持安全的并发读写,特别适合读多写少、且需要在遍历时删除元素的场景。
为什么 CopyOnWriteArrayList 能避免遍历中删除异常
它内部持有一个 volatile 数组引用,每次修改(add/remove/set)都会创建新数组并原子更新引用;而迭代器持有的是快照数组(构造时的副本),与后续写操作完全隔离。因此遍历时哪怕其他线程删了元素,也不会影响当前迭代器——它只看到创建时的数据状态。
正确使用方式:遍历时删除要靠迭代器自身方法
不能在增强 for 循环中直接调用 list.remove(),仍会出错。必须使用迭代器的 remove() 方法(该方法在 CopyOnWriteArrayList 中被重写为线程安全操作):
- ✅ 正确:用
Iterator.remove() - ❌ 错误:用
list.remove()或增强 for + remove
示例:
CopyOnWriteArrayList<string> list = new CopyOnWriteArrayList(Arrays.asList("a", "b", "c", "d"));
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s) || "c".equals(s)) {
it.remove(); // 安全:调用的是 CopyOnWriteArrayList.Iterator.remove()
}
}
// 此时 list = ["a", "d"]
</string></string>
注意它的适用边界和代价
CopyOnWriteArrayList 不是万能替代品,需权衡以下几点:
- 写操作开销大:每次增删都要复制整个数组,大数据量时内存和 CPU 消耗明显
- 数据一致性弱:读线程看不到最新写入,适用于“最终一致”场景,不适用于强实时性要求
- 不支持批量结构变更:如
clear()也是复制空数组,但频繁清空仍不高效 - 迭代器不支持
add()和set():其remove()是唯一支持的修改操作
替代方案对比(按场景选)
若不符合“读远多于写”的前提,可考虑:
- 读写都较频繁 → 用
ConcurrentHashMap模拟列表逻辑(如用序号作 key) - 需强一致性 + 遍历中删 → 加锁保护普通
ArrayList(如用synchronized或ReentrantLock) - 仅需安全遍历(不删不改)→
Collections.unmodifiableList+ 外部同步写
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











