直接遍历集合时调用remove()会抛出ConcurrentModificationException,这是Java集合快速失败机制所致;安全删除方式包括:删单个用list.remove(Object),批量删用removeIf(Predicate),复杂逻辑用Iterator.remove()。

直接在遍历集合时调用 remove() 会抛出 ConcurrentModificationException —— 这不是 bug,是 Java 集合的快速失败(fail-fast)机制在起作用。
为什么不能边遍历边用 ArrayList.remove() 删除?
因为 ArrayList 的迭代器内部维护了一个 modCount 计数器,每次调用 add() 或 remove() 会更新它;而 Iterator.next() 会校验该值是否被意外修改。手动调用 list.remove() 绕过了迭代器,触发校验失败。
- 错误写法:
for (String s : list) { if (s.isEmpty()) list.remove(s); } - 现象:运行时报
ConcurrentModificationException - 例外:
CopyOnWriteArrayList允许这样操作,但它是为读多写少并发场景设计的,普通业务中不推荐替代ArrayList
安全删除的三种主流方式
选哪种取决于你要删一个、多个,还是按条件批量删,以及是否需要保留原集合引用。
- 删单个已知元素:用
list.remove(Object)(注意是对象值,不是索引),或set.remove(Object)—— 这最简单,不涉及遍历 - 按条件批量删除(推荐):用
removeIf(Predicate),JDK 8+ 原生支持list.removeIf(s -> s == null || s.trim().isEmpty());
它内部使用迭代器的remove(),线程安全且语义清晰 - 需要更复杂逻辑(比如要获取被删元素做后续处理):显式使用
IteratorIterator
it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.length() > 10) it.remove(); // 只能用 it.remove()
}
removeIf() 和手动 Iterator 的性能与兼容性差异
removeIf() 底层也是基于 Iterator,但封装了逻辑,代码更短;手动 Iterator 更灵活,比如可以 break 或记录删除位置。
立即学习“Java免费学习笔记(深入)”;
- 性能:两者无本质差别,都是 O(n),但
removeIf()在ArrayList上做了优化(批量移动元素,减少拷贝次数) - 兼容性:
removeIf()要求 JDK ≥ 8;老项目若还在用 JDK 7,只能选Iterator方式 - 注意点:
removeIf()对LinkedList效率不高(链表随机访问慢),而Iterator遍历链表本身是高效的
真正容易被忽略的是:删除后集合大小变化会影响后续索引操作 —— 如果你非要用传统 for 循环加索引删除(比如 for (int i = 0; i ),必须倒序遍历,否则会漏删相邻元素。但这属于反模式,除非有特殊约束,否则别这么干。










