不能直接调用集合的remove()方法在iterator遍历过程中,因为会触发concurrentmodificationexception;正确做法是仅使用iterator.remove(),且须在next()后立即调用,否则抛illegalstateexception。

为什么不能直接调用集合的remove()方法
在Iterator遍历过程中,如果用list.remove()或set.remove()删元素,会触发ConcurrentModificationException——这不是并发问题,而是迭代器检测到集合结构被意外修改了。Java 的 fail-fast 机制会立即报错,防止出现不可预测的状态。
根本原因是:大多数集合(如ArrayList、HashSet)的迭代器内部维护一个modCount计数器,每次调用add()、remove()等结构性修改方法时,集合会更新它;而迭代器在next()前会校验这个值是否匹配。手动调集合的remove()会让两者失步。
- 错误写法:
for (String s : list) { if (s.isEmpty()) list.remove(s); } - 正确路径只有一条:必须通过当前
Iterator对象的remove()方法 -
Iterator.remove()是唯一被允许的删除方式,它会同步更新modCount和迭代器状态
Iterator.remove()的使用前提和限制
这个方法不是万能的,它有明确的前置条件和生命周期约束。
- 必须在调用过
next()之后才能调用remove(),否则抛IllegalStateException - 每个
next()最多只能配一个remove(),重复调用会再次抛IllegalStateException - 不支持在
forEachRemaining()之后调用remove()(因为没经过next()) -
LinkedList和ArrayList都支持,但某些只读或代理集合(如Collections.unmodifiableList()返回的)会直接抛UnsupportedOperationException
示例安全写法:
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s == null || s.trim().isEmpty()) {
it.remove(); // ✅ 正确:紧接 next() 后调用
}
}</string>
增强for循环里怎么安全删?别硬刚
增强for本质就是语法糖,底层仍用Iterator,但它把迭代逻辑封装死了,你没法拿到Iterator引用。所以想在for (x : coll)里删元素,只有两个现实选择:
- 改用显式
Iterator循环(推荐)——控制力强、意图清晰、无歧义 - 收集待删元素,遍历完再批量删:
List<string> toRemove = new ArrayList(); for (String s : list) if (s.isEmpty()) toRemove.add(s); list.removeAll(toRemove);</string> - 注意:
removeAll()对ArrayList是O(n×m)复杂度,大数据量时比单次Iterator.remove()慢得多 - 别用
list.removeIf(predicate)?可以,但它是 Java 8+ 提供的替代方案,底层也是用Iterator,语义更简洁,但调试时堆栈不如手动迭代清晰
多线程环境下Iterator.remove()还安全吗
不安全。前面说的ConcurrentModificationException只是单线程下的保护机制;一旦多个线程同时读写同一个集合,即使全程用Iterator.remove(),也会出现数据丢失、跳过元素、甚至NullPointerException。
- 典型现象:一个线程在
next()后还没来得及remove(),另一个线程已删掉该位置元素,导致next()返回null或越界 - 解决方案不是加锁迭代器,而是换集合类型:
CopyOnWriteArrayList适合读多写少场景,它的Iterator是快照式的,remove()操作不影响正在遍历的迭代器 - 但
CopyOnWriteArrayList.iterator().remove()会抛UnsupportedOperationException——它的迭代器不支持修改,真要删得用集合自身的remove() - 真正需要线程安全且支持边遍历边删的,考虑
ConcurrentLinkedQueue配合poll(),或用ReentrantLock保护整个遍历逻辑
最常被忽略的一点:很多人以为“用了Iterator.remove()就线程安全了”,其实它只解决单线程结构一致性,完全不提供并发保护。










