Java中遍历HashSet时需用Iterator.remove()或removeIf()安全删除,避免ConcurrentModificationException;多线程场景应选ConcurrentHashMap.newKeySet()等线程安全方案。

在 Java 中对 HashSet 遍历时直接调用 remove() 会抛出 ConcurrentModificationException,因为普通 for 循环或增强 for 循环底层使用的是 Iterator,而它默认是“快速失败”(fail-fast)的。要安全删除,必须通过迭代器自身的 remove() 方法,或改用线程安全/支持并发修改的结构。
使用 Iterator 的 remove() 方法(推荐)
这是最常用、最标准的安全删除方式。遍历时获取迭代器,调用其 remove(),而非集合本身的 remove():
- 确保只在
next()调用之后、且仅调用一次remove() - 不能在循环外或连续多次调用
remove(),否则抛IllegalStateException - 示例:
HashSet<String> set = new HashSet<>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s) || "c".equals(s)) {
it.remove(); // ✅ 安全
}
}
使用 removeIf()(Java 8+,简洁高效)
Collection.removeIf(Predicate) 内部已封装了安全的迭代逻辑,语义清晰,代码更简短:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 底层仍基于迭代器,但对开发者透明
- 适合单条件批量删除,比如按规则过滤
- 示例:
set.removeIf(s -> "b".equals(s) || "c".equals(s)); // ✅ 一行搞定
避免常见错误写法
以下操作都会导致异常或逻辑错误:
立即学习“Java免费学习笔记(深入)”;
- 增强 for 循环中调用
set.remove(x)→ConcurrentModificationException - 普通 for +
get(i)(HashSet无索引访问,编译不通过) - 先收集待删元素,再遍历删除(如
new ArrayList(set))→ 可能漏删或重复删,且多一次遍历 - 用
CopyOnWriteArraySet替代(仅适用于读多写极少场景,性能开销大,且不解决原问题本质)
需要并发修改时的替代方案
如果多个线程同时读写,单纯用 Iterator.remove() 不够 —— 它只保证单线程迭代安全。此时应考虑:
-
ConcurrentHashMap.newKeySet()(Java 8+):返回线程安全的Set,支持并发迭代与修改 - 手动加锁(如
synchronized(set)块内遍历+删除),但会降低并发度 - 将删除逻辑转为“标记+重建”,例如流式过滤后重新赋值:
set = set.stream().filter(...).collect(Collectors.toSet());

















