增强for循环是遍历Set的首选方式,语法简洁且安全;需边遍历边删除时必须用Iterator.remove();Stream适合函数式操作但不修改原集合;toArray()仅在特定兼容场景使用。

用增强for循环遍历Set最常用也最安全
Java中Set本身不保证顺序(除非是LinkedHashSet或TreeSet),但遍历逻辑和List一致。增强for循环是首选,它底层调用Iterator,自动处理并发修改检查,且语法简洁:
Setset = new HashSet<>(); set.add("a"); set.add("b"); for (String s : set) { System.out.println(s); // 输出顺序不确定 }
注意:不能在循环体中直接调用set.remove(),否则抛ConcurrentModificationException;如需边遍历边删,必须用Iterator.remove()。
用Iterator手动遍历才能安全删除元素
当需要在遍历过程中动态移除满足条件的元素时,必须显式获取Iterator并调用其remove()方法——这是唯一被允许的删除方式:
Iteratorit = set.iterator(); while (it.hasNext()) { String s = it.next(); if (s.startsWith("a")) { it.remove(); // ✅ 安全删除 } }
-
it.remove()只能在next()之后调用一次,重复调用会抛IllegalStateException - 不能用
set.remove()替代,哪怕对象相等也不行 - 对
ConcurrentHashMap.newKeySet()这类并发集合,应改用removeIf()或专用迭代器
Stream遍历适合过滤/映射后的一次性消费
Java 8+ 可用stream()做函数式遍历,适合链式操作,但要注意它不改变原集合,且每次调用都新建流:
立即学习“Java免费学习笔记(深入)”;
set.stream() .filter(s -> s.length() > 1) .map(String::toUpperCase) .forEach(System.out::println);
常见误区:
- 不要在
forEach()里修改原Set,这属于副作用,既不安全也不符合函数式原则 - 若需收集结果,用
collect(Collectors.toSet()),别试图复用原引用 -
parallelStream()对小集合反而慢,且HashSet无序特性会让并行结果更难预测
toArray()转数组遍历只在特定场景有用
把Set转成数组再遍历,通常只为兼容老接口或需要随机访问索引。但代价是额外内存分配和失去泛型类型信息(除非用带泛型参数的重载):
// ❌ 不推荐:丢失类型,可能ClassCastException Object[] arr = set.toArray(); // ✅ 推荐:保留类型 String[] arr2 = set.toArray(new String[0]); // JDK 11+ 可传空数组
真正需要它的场景极少,比如调用某个只接受String[]的遗留JNI方法。日常开发中,优先走Iterator或Stream路径。
最容易被忽略的是:HashSet的遍历顺序与插入顺序无关,哪怕两次遍历同一组数据,输出也可能不同——这不是bug,是它的设计契约。










