遍历集合时删除元素必须用Iterator.remove(),因其同步更新expectedModCount与modCount;Collection.remove()仅改modCount致ConcurrentModificationException。

遍历集合时删元素,必须用 Iterator.remove(),不能用 Collection.remove()——这不是线程问题,单线程下也会抛 ConcurrentModificationException。核心在于两者对修改计数器(modCount)的处理完全不同。
底层机制:modCount 与 expectedModCount 的同步性
ArrayList、HashSet 等集合内部维护一个 modCount 字段,记录结构被修改的次数。迭代器创建时会把当前 modCount 值复制为自己的 expectedModCount。每次调用 next() 或 hasNext() 前,都会检查二者是否一致;不一致就立即报错。
-
Collection.remove(obj) 是外部方法:调用后只更新集合的
modCount,但迭代器的expectedModCount没变 → 下次next()就触发异常 -
Iterator.remove() 是迭代器自身方法:删除后不仅调用集合的删除逻辑(如
ArrayList.this.remove(lastRet)),还会紧接着把expectedModCount同步设为当前modCount→ 计数器保持一致,不会报错
使用前提和限制
Iterator.remove() 不是随便能调的,它依赖迭代器当前状态:
- 必须在调用过
next()之后才能调用,否则因lastRet < 0抛IllegalStateException - 每次
next()后最多只能调一次remove(),重复调用也会报错 - 它删除的是上一次
next()返回的那个元素,不是按值匹配,也不搜索
为什么 Collection.remove(obj) 即使“手动记下元素”也失败?
哪怕你写成这样:
立即学习“Java免费学习笔记(深入)”;
Object obj = iterator.next();
collection.remove(obj); // ❌ 依然报错
原因很直接:
-
collection.remove(obj)内部仍会修改modCount - 它完全不感知迭代器存在,更不会去更新任何
expectedModCount - 只要集合结构一变,下一轮迭代检测就通不过
替代方案:不用迭代器 remove 怎么安全删?
如果坚持用集合自身的 remove,有几种绕开方式:
- 反向遍历索引(适用于 List):从
size()-1往 0 遍历,用remove(int index),避免下标偏移 - 收集待删元素,遍历完再统一删:先
new ArrayList()存要删的对象,再调collection.removeAll(list) - 用
removeIf(Predicate):这是 JDK 8+ 提供的安全批量删除方法,内部已规避 fail-fast 问题


















