必须同时重写equals()和hashCode()方法,因HashSet基于HashMap实现:先用hashCode()定位桶,再用equals()在桶内比较;若二者不一致,逻辑相等的对象可能落入不同桶而无法去重。

要让自定义对象在 HashSet 中正确去重,必须同时重写 equals() 和 hashCode() 方法。只改一个会导致集合行为异常——比如内容相同的对象被当成两个不同元素重复添加。
为什么必须两个方法一起重写
HashSet 底层用的是 HashMap,它判断元素是否重复分两步:
- 先调用
hashCode()算出对象该放在哪个“桶”里 - 再在同一个桶内,用
equals()逐个比对是否真正相等
如果两个对象逻辑上相等(o1.equals(o2) == true),但 hashCode() 返回值不同,它们就会被放进不同的桶,第二步根本不会执行,结果就是“明明一样却没去重”。
重写 equals() 的关键步骤
目标是按字段值比较,而不是内存地址。按顺序处理更安全:
立即学习“Java免费学习笔记(深入)”;
- 用
this == obj快速判断是否为同一实例 - 检查
obj == null,避免空指针 - 用
getClass() != obj.getClass()判断类型是否一致(不用instanceof,防止子类破坏对称性) - 强转后比较字段:基本类型直接比,引用类型用
Objects.equals(a, b),数组用Arrays.equals()
重写 hashCode() 的核心原则
哈希码只依赖 equals() 中实际参与比较的字段:
- 字段不变,哈希码就不能变;字段一变,哈希码必须跟着变
- 推荐用
Objects.hash(field1, field2, ...),它自动处理null和散列组合 - 不要在
hashCode()里调用可能改变状态的方法或访问可变字段
验证是否写对的小技巧
写完别急着用,快速跑几个边界情况:
-
obj.equals(obj)必须返回true -
obj.equals(null)必须返回false - 两个内容完全相同的对象:
o1.equals(o2) == true且o1.hashCode() == o2.hashCode() - 把对象放进
HashSet,再加一个内容相同的对象,集合大小不应增加


















