直接调用集合的remove()或add()会触发fail-fast机制抛出concurrentmodificationexception,因modcount与expectedmodcount不匹配;正确做法是使用iterator.remove()、倒序遍历、收集后批量删除或线程安全集合。

因为在遍历过程中直接调用集合自身的 remove() 或 add() 方法,会破坏迭代器对集合状态的跟踪,触发 Java 的 fail-fast(快速失败)机制,从而抛出 ConcurrentModificationException 异常。
核心原因:modCount 与 expectedModCount 不匹配
Java 集合(如 ArrayList、HashMap)内部维护一个 modCount 计数器,记录集合结构被修改的次数。当你调用 collection.iterator() 时,迭代器会把当时的 modCount 值保存为 expectedModCount。之后每次调用 iterator.next() 前,都会校验两者是否一致。
- 你用
list.remove(x)删除元素 →modCount立即 +1 - 但迭代器仍持有旧的
expectedModCount - 下一次
next()检查失败 → 直接抛异常
增强 for 循环本质也是迭代器
写法 for (String s : list) 看似简单,底层其实是编译成:
while (it.hasNext()) { String s = it.next(); ... }
所以它完全受迭代器规则约束 —— 不能在循环体里调用 list.remove(),否则一样报错。
为什么设计成这样?不是太严格了吗?
这种机制不是为了“卡你”,而是保障遍历行为的确定性和安全性:
- 防止数据不一致:比如删除中间元素后,后续元素前移,迭代器可能跳过下一个元素,或重复访问同一位置
- 避免无限循环:某些场景下(如链表中误删节点),迭代器可能永远无法到达末尾
- 线程安全基础:单线程下先暴露问题,比多线程中静默出错更可控
安全删除的正确姿势
不是不能删,而是要走“配套通道”:
- ✅ 使用
iterator.remove():这是唯一被允许的、能同步更新expectedModCount的方式 - ✅ 倒序 for 循环(仅限 List):从
size-1往 0 遍历,删元素不影响前面索引 - ✅ 收集待删项,遍历完再统一删:
list.removeAll(toRemove) - ✅ 多线程场景用线程安全集合:
CopyOnWriteArrayList或加锁










