遍历集合时必须用iterator.remove()而非collection.remove(),根本原因是fail-fast机制下modcount与expectedmodcount需保持一致:前者同步更新计数器,后者导致不一致而抛concurrentmodificationexception。

遍历集合时删元素,必须用 iterator.remove(),不能用 collection.remove(),根本原因在于两者对集合修改计数器(modCount)的处理方式不同——这不是线程安全问题,单线程下也会出错。
fail-fast 机制是核心约束
ArrayList、HashSet 等集合内部维护一个 modCount 字段,记录结构被修改的次数。迭代器在创建时会把当前 modCount 值复制为自己的 expectedModCount。每次调用 next() 或 hasNext() 前,都会检查二者是否一致。一旦不等,立刻抛 ConcurrentModificationException。
-
collection.remove(obj)是外部方法,调用后只更新集合的modCount,但迭代器的expectedModCount没变 → 下次next()就触发异常 -
iterator.remove()是迭代器自己实现的方法,删除后会同步把expectedModCount设为当前modCount→ 计数器保持一致,不会报错
iterator.remove() 底层其实也调集合的 remove
它并不是绕开集合逻辑的“特殊通道”。以 ArrayList 的迭代器为例,其 remove() 方法源码中明确调用了 ArrayList.this.remove(lastRet) —— 也就是集合本身的按索引删除。关键区别在于:它在调用集合 remove 后,紧接着更新了 expectedModCount = modCount。
- 所以不是“没改集合”,而是“改了且同步了状态”
- 这也解释了为什么它只能在
next()之后调一次:lastRet记录的是上一次next()返回的索引,没调过next()就调remove()会因lastRet 抛 <code>IllegalStateException
为什么不能先 next 再 collection.remove?
即使你手动记下刚拿到的元素,再用集合的 remove(obj) 删除,依然会失败。因为:
-
remove(obj)是按值查找并删除,内部仍会修改modCount - 它不关心迭代器状态,也不更新
expectedModCount - 只要集合结构变了,下一轮迭代检测就通不过
替代方案要分场景看
如果不用迭代器,还有其他安全删除方式,但有适用前提:
- 对
List(如 ArrayList)可用倒序 for 循环:for (int i = list.size()-1; i >= 0; i--) { if (needRemove(list.get(i))) list.remove(i); }—— 因为删后面不影响前面索引 - 想批量删,可用
removeIf(Predicate)(Java 8+),它内部封装了安全的迭代逻辑 - Set 或 Map 不支持索引操作,倒序不可行,必须依赖迭代器或
removeIf
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











