CopyOnWriteArrayList 的迭代器不支持 remove() 是设计使然:其迭代器基于不可变快照,snapshot 为 final 且只读,调用 remove() 直接抛 UnsupportedOperationException;删除必须通过 list.remove() 或 removeIf() 触发数组复制,保障遍历零锁与弱一致性。

CopyOnWriteArrayList 的迭代器不支持 remove(),不是因为“忘了实现”,而是设计上刻意禁用——它根本不能也不该在迭代过程中删元素。
迭代器基于不可变快照
调用 iterator() 时,COWIterator 会立即拷贝当前数组(即“快照”),后续所有遍历都基于这份静态副本。这个副本一旦生成就不再更新,也不允许被修改。
- 快照是只读的:底层
snapshot字段声明为final,且没有写入逻辑 -
remove()方法直接抛UnsupportedOperationException,连校验都不做 - 哪怕你调用的是
it.remove(),它也不是在操作原列表,而是在试图改快照——这违背了 Copy-On-Write 的一致性模型
删除必须作用于原列表,但和迭代无关
真正能删元素的只有 list.remove(Object) 或 list.removeIf(Predicate),它们会触发整个数组复制流程:生成新数组、过滤/移除目标、替换引用。
- 这些操作对当前迭代器完全透明:快照仍存在,遍历继续按旧数据走
- 所以你在循环里调
list.remove(x)是安全的,但下一轮next()还是看到原来的元素 - 这不是 bug,是弱一致性保障——迭代器承诺“看到某一时刻的全部状态”,不承诺实时同步
为什么不允许迭代器自己删?
如果允许 it.remove(),就会面临两个无法解决的矛盾:
- 语义冲突:删的是快照还是原列表?删快照没意义(其他线程看不到);删原列表又破坏快照一致性
-
并发风险:多个迭代器同时调
it.remove(),谁该负责复制数组?锁在哪里加?这会把无锁读的优势全毁掉 -
设计取舍:CopyOnWriteArrayList 的定位是“读极多、写极少、容忍延迟”,不是通用容器。牺牲
it.remove()换来遍历零锁、绝对线程安全,很划算
实际编码中怎么安全删除?
别在 for-each 或 iterator.hasNext() 循环里调 it.remove(),那是死路一条。正确做法只有两种:
- 用
list.removeIf(predicate)一次性清理,语义清晰、线程安全、性能可控 - 先收集要删的元素(如用
new ArrayList()),再统一调list.removeAll(collected) - 极端情况需要边遍历边删且强实时性——说明不该用 CopyOnWriteArrayList,换
ConcurrentHashMap或加锁的ArrayList
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











