根本原因是未重写hashcode()或hashcode()与equals()不一致;hashset基于hashmap,先用hashcode()定位桶,若相等对象哈希值不同则不会调用equals(),导致重复添加。

Java 中用 HashSet 存储自定义类对象时出现“本该去重却重复添加”的问题,根本原因几乎总是:**没重写 hashCode() 方法,或 hashCode() 与 equals() 不一致**。
为什么没重写 hashCode 就会导致重复?
HashSet 底层基于 HashMap 实现,添加元素时会先计算对象的 hashCode(),再根据哈希值决定存入哪个桶(bucket)。如果两个逻辑上相等的对象(equals() 返回 true)返回不同的 hashCode(),它们大概率被分配到不同桶中,HashSet 就不会调用 equals() 比较,从而认为是两个不同元素——结果就是重复存入。
注意:Object.hashCode() 默认返回对象内存地址的整数表示,每个新实例都不同。所以即使你重写了 equals(),只要没同步重写 hashCode(),就一定会出问题。
如何正确重写 equals 和 hashCode?
必须同时重写,且满足以下原则:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 如果两个对象
equals()返回true,它们的hashCode()必须相同; - 如果两个对象
hashCode()相同,equals()不一定为true(允许哈希碰撞); - 在对象生命周期内,只要用于
equals()比较的字段没变,hashCode()返回值就不能变(尤其避免用可变字段如ArrayList或未初始化字段参与计算)。
推荐做法:用 IDE 自动生成(如 IntelliJ 的 Alt+Insert → equals() and hashCode()),选中所有参与逻辑相等判断的字段(比如 id、name),不要漏掉、也不要多加无关字段。
快速排查步骤
遇到重复问题,按顺序检查:
- 确认是否重写了
equals()—— 如果没重写,直接用Object.equals()(比较引用),那根本不可能“逻辑相等”,也就谈不上去重失败; - 确认是否重写了
hashCode()—— 查看类中是否有该方法,且不是直接return super.hashCode();; - 对比
equals()和hashCode()使用的字段是否完全一致 —— 常见错误:equals()比较id + name,hashCode()只用了id; - 运行简单测试验证:
Person p1 = new Person("001", "Alice");
Person p2 = new Person("001", "Alice");
System.out.println(p1.equals(p2)); // 应为 true
System.out.println(p1.hashCode() == p2.hashCode()); // 必须为 true
Set<person> set = new HashSet();
set.add(p1); set.add(p2);
System.out.println(set.size()); // 应为 1
</person>
其他容易忽略的细节
除了重写问题,这些情况也会干扰预期行为:
-
使用了可变字段:比如
hashCode()依赖一个后期会修改的String name字段,对象加入HashSet后改名,再查找/删除可能失败(因为哈希桶位置变了); -
继承关系处理不当:子类重写
equals()但没同步更新hashCode(),或违反对称性(如A.equals(B)为true,但B.equals(A)为false); -
IDEA 提示没用:有些 IDE 在检测到
equals()被重写但hashCode()缺失时会标黄警告,别忽略它。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










