面试知识库
极高 进阶

equals与hashCode#

一句话答案#

重写 equals 必须重写 hashCode,相等对象 hashCode 必须相同,HashMap 先比较 hashCode 再比较 equals。

核心要点

原因:equals()hashCode() 之间有契约(Contract):

如果两个对象 equals() 返回 true,则它们的 hashCode() 必须相同。

如果只重写 equals() 不重写 hashCode()

HashMap 的查找流程:

  1. 计算 key 的 hashCode() → 定位桶位置
  2. 在桶中用 equals() 逐个比较

如果 hashCode() 不一致,第一步就定位错误,永远找不到对应的 key。

正确做法:

@Override
public int hashCode() {
    return Objects.hash(name);  // 基于 name 计算,保证 equals 相同时 hashCode 也相同
}
java
面试回答(2分钟版)

equals和hashCode之间存在一个核心契约:如果两个对象equals返回true,它们的hashCode必须相同。这个契约的根本原因在于HashMap的查找机制——先用hashCode定位到数组中的桶位置,再在桶内用equals逐个比较找到目标元素。如果只重写equals不重写hashCode,两个逻辑相等的对象可能hashCode不同,导致它们被分配到不同的桶中,用一个对象put进去后用另一个逻辑相等的对象get时根本定位不到正确的桶,返回null。Object默认的hashCode基于内存地址计算,所以不重写的话两个new出来的对象即使内容一样hashCode也不同。正确做法是使用Objects.hash()方法基于参与equals比较的字段来计算hashCode,保证equals相等时hashCode一定相同。反过来hashCode相同equals不一定相同,这就是哈希碰撞。还有一个容易被忽略的问题:如果一个对象作为HashMap的key放入后,其参与hashCode计算的字段被修改了,会导致再也找不到这个entry,造成内存泄漏,所以HashMap的key最好用不可变对象。

追问与易错

追问方向:

  • “为什么重写 equals 必须重写 hashCode?”→ HashMap 查找时先用 hashCode 定位桶位置再用 equals 逐个比较,若 equals 相等但 hashCode 不同会定位到错误的桶,get 返回 null
  • “hashCode 相同 equals 一定相同吗?”→ 不一定,这就是哈希碰撞。hashCode 相同只说明在同一个桶中,还需要 equals 进一步判断是否是同一个 key
  • “HashMap 中 key 的 hashCode 变了会怎样?”→ 已存入的 entry 无法被找到(因为 hash 值变了定位到不同的桶),造成内存泄漏,所以 HashMap 的 key 最好用不可变对象

易错点:

  • ❌ 反过来说”equals 相等 hashCode 可以不同”——这会违反契约,HashMap 行为异常
  • ❌ 忘记 null 值处理——equals 实现中要处理 null 和类型检查