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),它们会触发整个数组复制流程:生成新数组、过滤/移除目标、替换引用。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 这些操作对当前迭代器完全透明:快照仍存在,遍历继续按旧数据走
- 所以你在循环里调
list.remove(x)是安全的,但下一轮next()还是看到原来的元素 - 这不是 bug,是弱一致性保障——迭代器承诺“看到某一时刻的全部状态”,不承诺实时同步
为什么不允许迭代器自己删?
如果允许 it.remove(),就会面临两个无法解决的矛盾:
立即学习“Java免费学习笔记(深入)”;
- 语义冲突:删的是快照还是原列表?删快照没意义(其他线程看不到);删原列表又破坏快照一致性
-
并发风险:多个迭代器同时调
it.remove(),谁该负责复制数组?锁在哪里加?这会把无锁读的优势全毁掉 -
设计取舍:CopyOnWriteArrayList 的定位是“读极多、写极少、容忍延迟”,不是通用容器。牺牲
it.remove()换来遍历零锁、绝对线程安全,很划算
实际编码中怎么安全删除?
别在 for-each 或 iterator.hasNext() 循环里调 it.remove(),那是死路一条。正确做法只有两种:
- 用
list.removeIf(predicate)一次性清理,语义清晰、线程安全、性能可控 - 先收集要删的元素(如用
new ArrayList()),再统一调list.removeAll(collected) - 极端情况需要边遍历边删且强实时性——说明不该用 CopyOnWriteArrayList,换
ConcurrentHashMap或加锁的ArrayList

















