根本原因是Java集合的fail-fast机制,非并发导致;单线程应使用Iterator.remove()、removeIf()或倒序遍历;多线程才考虑ConcurrentHashMap等并发集合或同步控制。

Java里for循环(尤其是增强for)遍历时修改集合,抛ConcurrentModificationException,根本原因不是“并发”,而是Java集合的fail-fast机制——只要结构被意外修改(比如add/remove),迭代器就立刻报错。解法要分清单线程还是多线程场景,不能一概套用“加锁”或“换并发集合”。
单线程下:别在遍历时直接调用list.remove()
增强for循环本质就是Iterator,内部有modCount校验。一边遍历一边用list.remove(),必然触发异常。
- ✅ 正确做法:用
Iterator.remove()——它会同步更新expectedModCount - ✅ 更推荐:Java 8+ 用
list.removeIf(predicate),一行搞定,语义清晰,底层仍走安全删除路径 - ✅ 或者:先收集待删项,遍历完再统一
list.removeAll(toRemove) - ❌ 禁止:在增强for里写
list.remove(x);也别在普通for正序遍历时删,否则下标会跳过元素
普通for循环删元素:倒序遍历更稳
如果必须用索引操作(比如要根据下标逻辑判断),正序删会导致后续元素前移、下标错位。倒序可避免这个问题。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- ✅ 倒序写法:
for (int i = list.size() - 1; i >= 0; i--) { if (shouldRemove(list.get(i))) list.remove(i); } - ✅ 同样适用:用
list.set(i, newValue)改元素值——这不算“结构修改”,不会触发异常
真有多线程?才考虑并发集合或同步控制
ConcurrentModificationException在多线程中确实更常见,但并发集合不是万能解药:比如CopyOnWriteArrayList适合读多写少,但写操作开销大;ConcurrentHashMap不支持在entrySet().iterator()里调用remove(),得用computeIfPresent等原子方法。
立即学习“Java免费学习笔记(深入)”;
- ✅ 多线程读写频繁 → 优先选
ConcurrentHashMap(Map场景)或ConcurrentLinkedQueue(队列) - ✅ 多线程读远多于写,且需List接口 →
CopyOnWriteArrayList,它的迭代器基于快照,遍历时增删不影响当前遍历 - ✅ 小范围临界区 → 用
synchronized(list)或ReentrantLock保护整个遍历+修改块(注意粒度,别锁太久) - ❌ 别把
CopyOnWriteArrayList用在写多场景,也别指望它解决单线程里的fail-fast问题
其他实用技巧
有些写法看着绕,但能避开陷阱,尤其适合复杂条件或流式处理。
- ✅ 用Stream过滤重建:
list = list.stream().filter(x -> !x.startsWith("tmp")).collect(Collectors.toList());(注意:这是新建List,原引用失效) - ✅ 遍历subList时特别小心——它和原List共享modCount,删subList也会让原List迭代器失效
- ✅ 调试时加日志:打印modCount变化,快速定位哪行代码偷偷改了集合

















