增强for循环中直接修改集合会抛出concurrentmodificationexception,这是java集合快速失败机制检测到结构性修改(如add、remove)所致;应使用iterator.remove()、收集后批量删除、倒序遍历或线程安全集合来安全修改。
在增强 for 循环(for-each)中直接修改集合(如 add、remove)会触发 concurrentmodificationexception,这不是“并发”导致的异常,而是 java 集合的**快速失败(fail-fast)机制**在检测到结构性修改时主动抛出的——它本质是帮你提前暴露**迭代期间非法修改集合结构**的风险。
为什么增强 for 循环中 remove/add 会抛出该异常
增强 for 循环底层依赖 Iterator。大多数集合(如 ArrayList、HashMap)的迭代器内部维护一个 expectedModCount 字段,初始化时等于集合的 modCount(记录结构性修改次数)。每次调用 next() 或 hasNext() 前,都会检查两者是否一致。一旦你在循环中调用 list.remove(obj) 或 list.add(...),modCount 自增,但迭代器的 expectedModCount 没同步更新,下一次迭代检查就失败,立即抛出异常。
哪些操作属于“结构性修改”
结构性修改指改变集合大小或内部组织结构的操作,包括:
-
add()、remove()、clear()、retainAll()、removeAll() -
ArrayList的ensureCapacity()触发扩容(间接影响结构) -
HashMap的put()引起 rehash(也属结构性变化) -
不包括:仅修改元素内容(如
list.set(i, x))、或对Iterator自身的remove()方法(它是安全的)
如何安全地边遍历边修改
关键原则:**避免绕过迭代器直接修改集合本身**。推荐方式有:
- 使用
Iterator.remove()—— 这是唯一被设计为在迭代中安全删除的方式
示例:
for (Iteratorit = list.iterator(); it.hasNext();) {
String s = it.next();
if (s.isEmpty()) it.remove(); // ✅ 安全
} - 收集待删元素,遍历结束后统一删除(如
removeAll(toRemove)) - 倒序遍历普通 for 循环(适用于
List):
for (int i = list.size() - 1; i >= 0; i--) {
if (shouldRemove(list.get(i))) list.remove(i); // ✅ 下标不会越界 - 使用线程安全集合(如
CopyOnWriteArrayList)——但注意其适用场景(读多写少),且它通过复制数组实现,不是解决逻辑错误,而是规避检查
该异常不是 bug,而是设计意图
ConcurrentModificationException 不代表代码“并发了”,而是在告诉你:当前逻辑存在**不可预测行为风险**。比如在 ArrayList 中边遍历边 add(),可能跳过元素、重复访问、甚至 ArrayIndexOutOfBoundsException。JVM 选择提前报错,而非静默出错,正是为了强制你显式处理修改逻辑。把它当作编译器级别的“运行时警告”,比事后调试诡异的数据丢失更高效。










