根本原因是未同时正确重写hashcode()和equals()方法:只重写equals()导致哈希值不同而无法定位桶,只重写hashcode()则equals()仍用默认引用比较;二者必须逻辑一致且基于相同不可变字段。

HashSet 存储自定义实体时去重失效,根本原因不是“HashSet 不工作”,而是它严格依赖你写的 hashCode() 和 equals() 方法。只要这两个方法没写对,哪怕对象内容一模一样,HashSet 也认不出是同一个东西。
必须同时重写 hashCode() 和 equals()
只改其中一个,等于白干:
- 只重写
equals():两个对象内容相同,但哈希值不同 → 被分到不同桶里,equals()根本不会被调用,重复添加成功 - 只重写
hashCode():哈希值一样了,但equals()还是用默认的引用比较 → 依然认为是不同对象 - 两个都重写,且逻辑一致:才能让 HashSet 先靠哈希快速定位,再靠 equals 精准确认
字段选择要稳定、一致
参与比较和哈希计算的字段,必须满足两个硬要求:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用于
equals()判断的字段,必须全部也用于hashCode()计算(反之亦然) - 这些字段最好不可变(比如用
final修饰),或者至少在对象加入 HashSet 后不再修改 - 如果非得改,得先
set.remove(obj),改完再set.add(obj),否则对象可能“消失”在旧桶里,再也找不到了
别踩这些典型坑
实际开发中高频出错点:
- 用了可变字段(比如一个会动态更新的
status字段)参与哈希和比较 → 对象进集合后状态一变,哈希值就变了 -
hashCode()里漏掉了某个equals()中用到的字段 → 相等对象哈希值不同 -
equals()没判 null,或没处理类型转换(比如传进来的是 Object,直接强转没检查instanceof)→ 运行时报NullPointerException或永远返回 false - IDE 自动生成时勾选了不该参与的字段(比如数据库主键 ID,但对象还没持久化,ID 是 0 或 null)→ 多个新对象哈希值全为 0,全挤在一个桶里,性能暴跌
推荐做法:用工具生成,别手写
手写容易漏、难维护。主流方案有:
- IDEA:右键 → Generate →
equals()andhashCode()→ 勾选真正决定“相等性”的字段(如name、idCardNo) - Lombok:加
@EqualsAndHashCode(of = {"name", "age"})注解,自动搞定 - Apache Commons Lang:用
EqualsBuilder.reflectionEquals()和HashCodeBuilder.reflectionHashCode()(适合简单场景,但反射有开销)










