根本原因是copyonwritearraylist在jdk 19前未重写removeif,其继承自abstractcollection的默认实现调用不支持remove的快照迭代器;jdk 19+已修复,改用线程安全的写时复制逻辑。

Java 中 Collection.removeIf 在 CopyOnWriteArrayList 上抛出 UnsupportedOperationException,根本原因在于该类的 removeIf 方法未被重写,而是继承自 AbstractCollection 的默认实现,而该默认实现内部调用了不支持的 Iterator.remove()。
CopyOnWriteArrayList 的迭代器不支持 remove 操作
CopyOnWriteArrayList.iterator() 返回的是一个“快照式”迭代器(COWIterator),其 remove() 方法直接抛出 UnsupportedOperationException。这是设计使然:为保证线程安全和遍历一致性,它不允许在遍历时修改底层数组。
removeIf 的默认实现(来自 AbstractCollection)依赖于迭代器的 remove() 方法逐个删除匹配元素,因此在 CopyOnWriteArrayList 上会触发异常。
Java 版本差异:JDK 8 不支持,JDK 19+ 已修复
这个问题在 JDK 8 和 JDK 11 等早期版本中普遍存在。直到 JDK-8277114 被修复,从 JDK 19 开始,CopyOnWriteArrayList 才正式重写了 removeIf 方法,使用线程安全的写时复制逻辑完成批量删除。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- JDK 8–18:
removeIf继承自AbstractCollection→ 调用iterator().remove()→ 抛异常 - JDK 19+:
removeIf被重写 → 遍历 + 收集索引 + 原子替换新数组 → 正常工作
兼容低版本 JDK 的替代方案
若项目仍运行在 JDK 17 或更早版本,可采用以下安全方式替代 removeIf:
-
手动收集待删元素再 removeAll:
List<e> toRemove = list.stream().filter(predicate).collect(Collectors.toList());<br>list.removeAll(toRemove);</e>
注意:removeAll在CopyOnWriteArrayList中是线程安全且支持的。 -
使用 for 循环 + 索引倒序删除(不推荐用于并发场景):
仅适用于单线程、确定无并发读写的临时场景,违背了CopyOnWriteArrayList的设计初衷。 -
改用普通 ArrayList + 外部同步(需权衡线程安全需求):
若并发写不频繁,可考虑用Collections.synchronizedList(new ArrayList())并自行控制同步块。
验证是否已修复的简单方法
可在运行时检查当前 CopyOnWriteArrayList 类是否重写了 removeIf:
System.out.println(CopyOnWriteArrayList.class.getDeclaredMethod("removeIf", Predicate.class).getDeclaringClass());
输出 class java.util.concurrent.CopyOnWriteArrayList 表示已重写;若报 NoSuchMethodException 或显示 AbstractCollection,说明仍在使用默认实现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










