HashSet高效源于复用HashMap哈希表结构,平均时间复杂度O(1);需正确重写hashCode与equals、预设容量、避免装箱及重复创建。

用对数据结构:别拿 ArrayList 做去重主力
反复调用 list.contains() 判断再添加,时间复杂度是 O(n) 每次,数据量一上去就明显变慢。而 HashSet 的 add() 方法内部直接走 HashMap.put(),哈希定位桶位置 + 少量 equals 比较,绝大多数场景下一步到位。
- 读文件去重 → 直接用 new HashSet(Arrays.asList(...)) 或逐行 add
- 临时去重后要遍历 → 不必先转 ArrayList,HashSet 本身支持 for-each 和迭代器
- 需要顺序 → 改用 LinkedHashSet,保留插入序且仍为 O(1) 均摊
自定义对象进 HashSet:hashCode 和 equals 必须配套重写
只重写 equals 而不重写 hashCode,会导致逻辑上相等的对象被散列到不同桶里,equals 根本不会被触发,重复元素就悄悄进来了;只重写 hashCode 不重写 equals,则可能把不同对象压进同一桶后无法区分,误判为重复。
- 用 Objects.hash(field1, field2) 生成 hashCode,简洁安全
- equals 中先做引用判断(this == obj),再类型检查,最后字段逐一对比
- IDE(如 IntelliJ)可一键生成,但务必确认字段选取合理(比如不含动态计算值或易变状态)
初始化容量与加载因子:大集合提前规划
默认初始容量是 16,加载因子 0.75。当元素数超过 capacity × loadFactor(即 12 个),就会触发扩容——重建哈希表、重新散列所有元素,代价不小。
- 预估最终大小(比如确定要存 1000 个元素)→ 构造时指定容量:new HashSet(1024)
- 不想算太细?按 2 的幂向上取整更稳妥(HashMap 内部会调整为最近的 2 的幂)
- 一般无需改加载因子,除非明确追求内存紧凑且能接受稍高冲突率
避免不必要的装箱和重复计算
基本类型数组转 HashSet 时,Integer[] 可直接构造;但 int[] 得先转 Integer[] 或用 IntStream.boxed(),否则会把整个数组当一个对象加进去。另外,不要在循环里反复 new HashSet,能复用就复用。
立即学习“Java免费学习笔记(深入)”;
- int[] arr = {1,2,2,3}; → Set
set = Arrays.stream(arr).boxed().collect(Collectors.toSet()); - 频繁去重操作 → 把 HashSet 提到方法外作为成员变量,清空用 set.clear() 而非新建
- lambda 中避免在 hashCode/equals 里调用耗时方法(如数据库查询、正则匹配)


















