唯一真正可靠的单线程方式是iterator.remove(),因其同步更新modcount与expectedmodcount;for-each和普通for循环中调集合remove()会触发concurrentmodificationexception;removeif()是java 8+推荐的简洁替代,底层仍基于iterator.remove()。

Java 中在遍历集合时安全删除元素,唯一真正可靠的单线程方式是使用 Iterator.remove(),而不是调用集合自身的 remove() 方法。这是因为 ArrayList、LinkedList、HashSet 等集合的迭代器采用 fail-fast 机制——一旦检测到结构被意外修改(比如你手动调了 list.remove()),就会立刻抛出 ConcurrentModificationException。
为什么不能在 for-each 或普通 for 循环里删
增强 for 循环(for (String s : list))本质是 Iterator 的语法糖,但你无法拿到迭代器对象,也就没法调 it.remove();若在循环体中写 list.remove(s),会直接触发异常。
普通正向 for 循环(for (int i = 0; i )虽不抛异常,但删除后后续元素前移,而索引仍自增,导致跳过紧邻的下一个元素。例如删掉索引 1 的项,原索引 2 的元素移到 1,但 <code>i 已变成 2,这个元素就被漏掉了。
Iterator.remove() 的正确用法
它不是“随便调就安全”,必须严格遵循执行节奏:
- 先调用
it.next()取出当前元素 - 再根据业务逻辑判断是否要删
- 满足条件时,立即调用
it.remove()(且只能在这一次next()后调一次) - 不能未调
next()就先remove(),也不能连续两次remove()(中间没next())
示例:删除字符串列表中长度超过 5 的元素
✅ 正确写法:
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.length() > 5) {
it.remove(); // 安全,紧跟 next() 后
}
}</string>
更简洁的替代方案:removeIf()
Java 8+ 提供了 Collection.removeIf(Predicate),底层就是封装好的 Iterator.remove(),语义清晰、代码简短,日常推荐优先使用:
list.removeIf(s -> s == null || s.trim().isEmpty());- 等价于上面的手动迭代写法,但不可用于需要记录被删元素或执行副作用操作的场景(比如打印日志、更新统计)
- 对 ArrayList 是 O(n) 时间复杂度,会触发数组收缩;对 LinkedList 也是 O(n),无额外拷贝开销
多线程环境下仍不安全
即使用了 Iterator.remove(),如果其他线程同时修改同一集合(比如另一个线程调 list.add()),依然可能抛 ConcurrentModificationException。这不是 bug,而是 fail-fast 的设计意图——它只保障单线程下的结构一致性。
并发场景下应改用线程安全集合,例如:
-
CopyOnWriteArrayList(适合读多写少) -
Collections.synchronizedList()配合外部同步块 -
ConcurrentHashMap的 keySet 或 values 视图(注意其迭代器是弱一致的)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











