必须同时重写 equals() 和 hashCode(),否则 HashMap、HashSet 无法正确识别逻辑相等对象:hashCode() 定位桶,equals() 桶内精确比对;若不一致,相等对象被分到不同桶,equals() 无机会执行。

只重写 equals() 不重写 hashCode(),或者反过来,会让 HashMap、HashSet 这类集合“认不出”逻辑上相等的对象——不是它们不努力找,而是根本没去对的地方找。
哈希表的两步查找机制决定了必须配合
HashMap 和 HashSet 底层都是哈希表,查找或插入一个对象时,严格分两步:
- 第一步:用 hashCode() 定位桶(数组索引)——这一步跳过所有不在这个桶里的对象,效率高但粗略;
- 第二步:在同一个桶里,用 equals() 逐个比对——这一步才做精确判断,确认是不是你要的那个对象。
如果两个对象业务上完全一样(a.equals(b) == true),但 a.hashCode() != b.hashCode(),它们就会被分配到不同桶中。后续调用 set.contains(b) 或 map.get(b) 时,系统只会去 b.hashCode() 对应的桶里找,压根不会翻看 a 所在的桶——equals() 根本没机会执行。
只重写 equals() 的典型故障表现
比如定义一个 User 类,只重写了 equals() 比较 name 和 age,但没动 hashCode():
立即学习“Java免费学习笔记(深入)”;
-
new User("李四", 30)和new User("李四", 30)调用equals()返回 true; - 但它们的
hashCode()是 Object 默认实现,基于内存地址,大概率返回不同值; - 放进
HashSet后,size()变成 2,去重失效; - 存进
HashMap当 key 后,用另一个相同内容的对象去get(),结果是 null。
只重写 hashCode() 同样不行
假设你给 User 写了个固定返回 1 的 hashCode(),但 equals() 没重写(还是默认的引用比较):
- 所有对象都挤进同一个桶,查找退化为链表遍历,性能暴跌;
- 哪怕两个对象字段一模一样,
equals()仍返回 false,集合依然认为它们不同; - 这不是功能错误,而是逻辑错位:哈希值统一了,但“相等”的定义没变。
正确做法的关键点
两者必须基于同一组不可变的业务字段计算,并满足契约:
- 只要
equals()返回 true,hashCode()就必须返回相同整数; - 推荐用
Objects.equals(a, b)替代a.equals(b),自动处理 null; - 推荐用
Objects.hash(name, age)生成哈希值,内部已做 null 判空和合理混合; - 参与比较和哈希的字段,一旦对象加入集合,就不要再修改——改了会导致位置“丢失”。


















