
HashSet 求两个集合的交集,最常用、最高效的方式是调用 retainAll() 方法 —— 它会直接修改原集合,只保留也存在于指定集合中的元素。
使用 retainAll() 原地求交集
这是标准做法,时间复杂度接近 O(n),底层利用 HashSet 的哈希查找优势:
- 确保操作的是可变的 HashSet(不能是不可变集合或 Collections.unmodifiableSet)
- 调用方集合会被修改,如果需要保留原集合,先复制一份
- 参数传入另一个集合(可以是 HashSet、ArrayList、甚至其他 Collection)
示例:
HashSet<string> set1 = new HashSet(Arrays.asList("a", "b", "c"));
HashSet<string> set2 = new HashSet(Arrays.asList("b", "c", "d"));
// 求 set1 ∩ set2,结果保留在 set1 中
set1.retainAll(set2); // set1 现在是 ["b", "c"]
</string></string>
不修改原集合:创建新交集
如果不想改变原有集合,推荐手动构造新 HashSet:
- 遍历较小的集合(优化性能),检查元素是否在另一个中存在
- 用
contains()判断,HashSet 的平均时间复杂度是 O(1) - 避免用
removeIf()或流式filter()配合 collect,虽可读但可能多遍历
示例:
HashSet<string> set1 = new HashSet(Arrays.asList("a", "b", "c"));
HashSet<string> set2 = new HashSet(Arrays.asList("b", "c", "d"));
HashSet<string> intersection = new HashSet(set1);
intersection.retainAll(set2); // 安全:intersection 是新集合
// 或者更明确地:
// HashSet<string> intersection = new HashSet();
// for (String s : set1) {
// if (set2.contains(s)) intersection.add(s);
// }
</string></string></string></string>
注意 null 和自定义对象
交集计算依赖 equals() 和 hashCode():
- 若元素为 null,HashSet 允许一个 null,交集逻辑正常工作
- 若元素是自定义对象,必须正确重写
equals()和hashCode(),否则即使内容相同也会被视为不同元素 - String、Integer 等 JDK 类已正确实现,可直接用
和 Stream API 对比(不推荐用于简单交集)
虽然能用 stream().filter(set2::contains).collect(Collectors.toSet()),但没必要:
- 额外创建 Stream 和中间对象,开销更大
- 无法保证返回类型是 HashSet(可能是 LinkedHashSet 或其他)
- 代码更长,可读性反而没
retainAll()直观
除非你已在做复杂流处理,否则优先选原生集合方法。











