必须先调用hashCode()再调用equals(),因为hashCode用于快速定位桶索引实现O(1)查找,仅当哈希值相同才在同桶内用equals精确比较;若不遵守,会导致查找失败或性能退化为O(n)。

哈希冲突发生时,先调用 hashCode(),再调用 equals()。这是 HashMap、HashSet 等基于哈希表的集合类内部查找和插入逻辑的固定顺序,不是可选流程,而是设计前提。
为什么必须先算 hashCode?
哈希表本质是一个数组,通过哈希码快速定位“桶”(bucket)索引。如果跳过这步直接遍历所有元素调用 equals,性能就退化成 O(n),完全失去哈希结构的意义。
- hashCode 决定对象该放在哪个数组下标位置(比如
index = hash % table.length) - 只要 hashCode 不同,对象一定不会在同一个桶里,equals 根本不会被触发
- 这个阶段是“粗筛”,靠整数运算,极快
什么情况下会走到 equals?
只有当两个对象的 hashCode 相同,且被映射到同一个桶中时,HashMap 才会在该桶对应的链表或红黑树中逐个调用 equals() 做精确比对。
- 例如:User("alice", 25) 和 User("alice", 26) 若未重写 hashCode,很可能得到不同哈希值 → 不进同一个桶 → equals 不执行
- 但如果它们的 hashCode 恰好相同(或重写后按 name 计算,导致哈希一致),就会进入同一链表 → 此时必须靠 equals 判断是否真为同一逻辑键
- equals 返回 true 表示键已存在,执行覆盖;返回 false 表示新键,追加到链表/树中
不遵守规则会出什么问题?
若只重写 equals 而不重写 hashCode,或两者逻辑不一致,会导致对象“找得到却取不出”、“重复添加”等诡异行为。
- user1 和 user2 逻辑相等(name/age 相同),但 hashCode 不同 → put 进去两个,get 却只能取到其中一个
- 反之,若 hashCode 总返回固定值(如 1),所有对象挤进同一个桶 → equals 被频繁调用,性能暴跌,退化为链表查找
- Lombok 的
@EqualsAndHashCode自动同步生成,就是为避免手动疏漏
实际调用链条长这样
以 map.get(key) 为例:
- 1. 计算
key.hashCode()→ 得到哈希值 - 2. 经扰动运算和取模 → 定位到数组 index
- 3. 若该位置为 null → 直接返回 null
- 4. 否则遍历该位置的节点(链表或树)→ 对每个 node.key 调用
key.equals(node.key) - 5. 一旦 equals 返回 true → 返回对应 value;遍历完都没匹配 → 返回 null

















