遍历Java集合时直接删元素会触发ConcurrentModificationException,根本原因是“快速失败”机制通过modCount与expectedModCount比对检测非法修改;安全删除方式包括Iterator.remove()(需先next()再remove())、removeIf()(Java 8+,简洁高效)、倒序for循环(仅适用于ArrayList)以及先收集后批量删除(灵活但占内存)。

遍历Java集合时直接删元素会触发ConcurrentModificationException,根本原因不是多线程,而是集合的“快速失败”机制——它通过比对modCount和expectedModCount来检测非法结构修改。安全删除的关键,是让删除动作与遍历逻辑协同,不破坏迭代器状态。
用Iterator.remove()(通用稳妥)
这是最标准、兼容性最强的方式,适用于ArrayList、HashSet、HashMap.entrySet()等所有支持迭代器的集合。
- 必须先调
next()拿到当前元素,再立刻调remove(),否则抛IllegalStateException - 每个
next()最多只能配一次remove(),重复调用会出错 - 不能在
for-each里用,因为它不暴露迭代器引用
示例(删掉所有值为"tmp"的字符串):
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("tmp")) {
it.remove(); // ✅ 安全
}
}
立即学习“Java免费学习笔记(深入)”;
用removeIf()(Java 8+,简洁高效)
一行代码搞定条件过滤,底层仍走Iterator.remove(),语义清晰、不易出错。
- 适合单条件批量删除,比如
list.removeIf(s -> s == null || s.isEmpty()) -
Map本身没removeIf,但它的keySet()、values()、entrySet()都支持 - 无法获取被删元素内容,如需记录日志或统计,得先用
stream().filter().collect()提取再删
示例(删掉map中所有value小于10的项):
map.entrySet().removeIf(entry -> entry.getValue() < 10);
倒序for循环(仅List,避免索引错位)
只适用于ArrayList这类支持随机访问的List,利用删除后“后面元素前移不影响前面索引”的特性。
- 从
size()-1开始,条件写i >= 0 - 删完不额外
i--,因为remove(i)已让后续元素自动前移 - 不推荐用于
LinkedList,随机访问效率低
示例(删掉所有null):
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) == null) {
list.remove(i); // ✅ 安全
}
}
先收集后统一删(灵活但占内存)
当删除逻辑复杂、需跨多个条件判断,或要保留被删元素做后续处理时,这个方式最自由。
- 用
ArrayList或HashSet暂存待删元素(key或对象本身) - 遍历完再调
collection.removeAll(toRemove)或逐个remove() - 注意:若删
Map,推荐用map.keySet().removeAll(keys),避免ConcurrentModificationException
示例(删掉key以"temp"开头或value为null的键值对):
Set<String> toRemove = new HashSet<>();
for (Map.Entry<String, Integer> e : map.entrySet()) {
if (e.getKey().startsWith("temp") || e.getValue() == null) {
toRemove.add(e.getKey());
}
}
map.keySet().removeAll(toRemove); // ✅ 批量安全


















