遍历HashSet时须用Iterator.remove()或removeIf()安全删除,禁用set.remove();多线程场景应选ConcurrentHashMap.newKeySet()或同步块保护。

在 Java 中,遍历 HashSet 时直接调用 set.remove() 会抛出 ConcurrentModificationException,因为普通 for-each 或迭代器遍历时,底层的 modCount 检查机制会发现结构被并发修改。要安全删除,必须使用迭代器自身的 remove() 方法,或改用支持并发修改的集合类型。
用 Iterator 的 remove() 安全删除
这是最常用、最推荐的方式:获取显式 Iterator,在调用 next() 后,**立即且仅一次**调用 iterator.remove()。
- 不能在两次
next()之间多次调用remove() - 不能和 for-each 循环混用(for-each 底层也是迭代器,但不暴露 remove 接口)
- 示例:
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String s = it.next();
if ("b".equals(s) || "d".equals(s)) {
it.remove(); // ✅ 正确:通过迭代器删除
}
}
用 removeIf() 批量条件删除(Java 8+)
Collection.removeIf(Predicate) 内部已封装了安全的迭代逻辑,语义清晰,代码简洁,适合按条件批量移除。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 底层仍使用迭代器 + remove(),无需手动管理
- 不可在 predicate 中修改集合本身(比如再调用 set.remove())
- 示例:
避免踩坑的常见错误写法
以下方式都会触发 ConcurrentModificationException 或逻辑错误:
立即学习“Java免费学习笔记(深入)”;
-
for-each 中调用 set.remove():
for (String s : set) { if (cond) set.remove(s); } -
普通 for 循环 + 索引访问:HashSet 无索引,
set.get(i)根本不存在 - 新建集合暂存再 removeAll:虽不报错,但多一次遍历和内存开销,非必要不推荐
- 用 CopyOnWriteArraySet 替代 HashSet:它是线程安全的,但性能差、不适用于高频修改场景,且不是 HashSet 的替代品
需要线程安全?选 ConcurrentHashMap 或 Collections.synchronizedSet
如果遍历和删除操作跨多个线程,单靠迭代器 remove 仍不够。此时应选用线程安全的集合:
-
ConcurrentHashMap.newKeySet()(Java 8+):返回线程安全的Set,支持安全迭代和删除,但迭代不保证强一致性(可能看不到最新添加的元素) -
Collections.synchronizedSet(new HashSet()):需手动同步整个迭代过程,例如:synchronized(set) { for (E e : set) ... } - 注意:即使用了线程安全集合,多线程遍历时若还做修改,仍建议用迭代器 remove 或 removeIf 保证原子性

















