hashset去重依赖hashcode定位与equals确认两步:先通过hashcode快速定位桶位置,再用equals比较内容;自定义类必须同时重写二者,否则去重失效。

HashSet 对数据去重,靠的是底层哈希表机制,不是“看起来一样就去重”,而是严格按 hashCode 定位 + equals 确认 两步走。只要这两步配合正确,重复元素自然被拦在门外;一旦断掉任一环,去重就失效。
去重的核心流程:先算哈希,再比内容
当你调用 set.add(obj) 时,HashSet 实际执行以下动作:
- 调用
obj.hashCode(),算出一个整数哈希值 - 用这个哈希值快速定位到内部数组(桶)的某个下标位置
- 如果该位置为空,直接存入;如果不为空,就遍历桶里已有的元素
- 对每个已有元素,先检查哈希值是否相等 —— 不等就跳过;相等再调用
obj.equals(已有元素) - 只有
equals返回true,才判定为重复,放弃添加
基本类型和包装类能直接去重
像 String、Integer、Long 这些 JDK 自带的类,已经重写了 hashCode 和 equals,逻辑是按内容比较的。所以:
-
new HashSet<string>().add("abc"); add("abc");</string>→ 第二次添加返回false,size 仍是 1 -
new HashSet<integer>().add(100); add(100);</integer>→ 同样只保留一个
自定义对象必须同时重写两个方法
如果你定义了一个 User 类,直接往 HashSet 里加两个字段值完全相同的实例,默认不会去重——因为 Object 的 hashCode 返回的是内存地址,equals 默认也是地址比较。
正确做法是:
- 用
@Override重写equals:判断关键字段(如 id、name、age)是否全部相等 - 用
@Override重写hashCode:确保equals返回true的对象,hashCode也必须相同(常用Objects.hash(field1, field2)) - 两个方法必须一起改,缺一不可;只改一个,会导致哈希定位错位或比较失效
实际使用建议
日常开发中,推荐这样写:
- 去重并不要求顺序 → 直接用
new HashSet(list) - 需要保持插入顺序 → 换成
LinkedHashSet - 既要唯一性又要排序 → 选
TreeSet(但要求实现Comparable或传入Comparator) - 别把可变对象(比如 ArrayList、HashMap)直接塞进 HashSet —— 后续修改内容会破坏哈希一致性,导致
contains失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











