相等的对象必须有相同的哈希码,但哈希码相同不意味着对象一定相等;这是HashMap、HashSet正常工作的底层铁律,因查找时先用hashCode定位桶,再用equals确认key,若不守约则导致查不到、删不掉。

面试时被问到 equals 和 hashCode 的“紧密契约”,别背定义,直接用一句话锚定核心:相等的对象必须有相同的哈希码,但哈希码相同不意味着对象一定相等。这句话不是口号,而是 HashMap、HashSet 这些集合能正常工作的底层铁律。
为什么说这是“紧密契约”而不是建议?
这不是 Java 的风格偏好,而是集合类的运行机制决定的:
- HashMap 插入或查找 key 时,先调用
hashCode()算出桶号(数组下标),只在那个桶里遍历 Entry - 桶内可能有多个 key(哈希冲突),这时才用
equals()逐个比对,确认是不是你要找的那个 key - 如果两个逻辑上相等的对象(比如 id 都是 100 的 User)
hashCode()返回不同值,它们会被分到不同桶里——equals()根本没机会执行,查就查不到,删也删不掉
面试时怎么举例说明“不守约”的后果?
用最简代码讲清问题,比复述文档更有力:
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
- 定义一个只重写了
equals()(按 id 判断相等)、但没重写hashCode()的 User 类 - 执行:
map.put(new User(1), "A"); map.get(new User(1)); - 结果是
null—— 因为两次 new 出的对象默认hashCode()不同,get 时去了错误的桶,压根没触发 equals 比较 - 补上
@Override public int hashCode() { return Objects.hash(id); },问题立刻消失
重写时最关键的实操原则
契约落地成代码,三条不能踩的线:
-
字段一致性:equals 里参与比较的字段,必须全部出现在 hashCode 计算中(比如用了 name 和 age 比较,hashCode 就得用
Objects.hash(name, age)) - 不可变性优先:尽量选 final 字段或业务上不会改的字段(如主键 id),避免对象入集合后字段变更导致 hash 码错位
-
用 Objects 工具类:别手写 31 * a + b,直接用
Objects.equals(a, b)和Objects.hash(f1, f2, ...),安全又简洁
顺便提一句:什么时候可以不重写?
如果这个类永远不会作为 HashMap 的 key 或 HashSet 的元素,那 hashCode 和 equals 就只是普通方法,不用管契约。比如纯数据传输对象(DTO)、只做参数传递的 VO,保持默认引用比较反而更清晰。但只要涉及集合操作,契约就自动生效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










