安全删除HashMap元素须用Iterator.remove()、entrySet().removeIf()或先收集key再removeAll;禁用遍历中直接调map.remove(),否则触发ConcurrentModificationException。

遍历 HashMap 时直接调用 map.remove() 会触发 ConcurrentModificationException,这不是多线程问题,而是 HashMap 的 fail-fast 机制在起作用——它通过 modCount 检测结构被非法修改。安全删除的关键是:**不绕过迭代器控制权,不破坏遍历与修改的同步关系**。
用 Iterator 的 remove() 方法(最通用可靠)
这是所有 JDK 版本都支持、语义最清晰的方式。Iterator 的 remove() 是唯一被设计用于“边遍历边删当前元素”的方法,它会自动同步更新内部状态,避免校验失败。
- 必须先调
next()获取当前项,再调remove();否则抛IllegalStateException - 每次只能删一个,不能连续调两次
remove() - 推荐遍历
entrySet(),方便同时访问 key 和 value
示例:
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() < 5) {
it.remove(); // ✅ 安全
}
}
用 entrySet().removeIf()(Java 8+,简洁高效)
Map 本身没 removeIf,但它的 keySet()、values() 和 entrySet() 都继承自 Collection,天然支持该方法。底层仍基于 Iterator,只是封装了逻辑。
立即学习“Java免费学习笔记(深入)”;
- 适合单条件批量删除,比如按值大小、key 前缀、是否为 null
- 代码简洁,一行搞定,可读性高
- 不适合需要在删除前执行复杂计算,或需保留被删元素信息的场景
示例:
map.entrySet().removeIf(entry -> entry.getKey().startsWith("tmp") || entry.getValue() == null);先收集 key 再统一 removeAll(适合多条件或预处理逻辑)
当判断逻辑分散、涉及多次计算、或要和另一个集合比对时,可先缓存待删 key,遍历结束后批量清理。虽多占一点内存,但逻辑解耦、不易出错。
- 用
HashSet存 key,避免重复导致误删 - 最后调
map.keySet().removeAll(keysToRemove),线程安全且无异常 - 注意:不要在遍历中逐个调
map.remove(key),依然会触发异常
示例:
Set<String> toRemove = new HashSet<>();for (Map.Entry<String, Integer> e : map.entrySet()) {
if (e.getValue() < 0 || isExpired(e.getKey())) {
toRemove.add(e.getKey());
}
}
map.keySet().removeAll(toRemove);
哪些方式要避免
以下写法看似自然,但都会抛 ConcurrentModificationException:
- 增强 for 循环中调
map.remove()(底层仍是 Iterator,但没走其 remove 接口) - 普通 for 循环配合
map.remove() - Stream 中调
map.remove()(如map.entrySet().stream().filter(...).forEach(e -> map.remove(e.getKey()))) - 用
CopyOnWriteArraySet包装entrySet()虽能规避异常,但属于“杀鸡用牛刀”——HashMap 本身非并发场景,没必要引入高开销的写时复制结构


















