java基础的面试高频04:equals 和 hashCode 为什么要配套重写?不重写 HashMap 会出现什么严重问题
equals()和hashCode()是 Object 类中两个顶级方法,几乎所有集合面试都会围绕这两个方法展开。很多同学只记住一句话:相等的对象必须哈希值相等,哈希值相等对象不一定相等,却答不出为什么必须成对重写,不重写在HashMap、HashSet中会出现什么 bug。今天结合哈希表底层原理讲清楚设计规范。
一、原生 equals 与 hashCode 默认实现
1. Object 原生 equals 方法
默认实现是使用==比较两个对象的内存地址,只有两个引用指向堆中同一个对象时才返回 true,无法实现我们自定义的 “业务相等”。
比如用户类,我们希望两个用户 id 相同就判定为同一个用户,原生 equals 无法实现,必须重写。
2. Object 原生 hashCode 方法
根据对象的内存地址,通过哈希算法生成一个 int 类型哈希值,不同对象地址不同,哈希值大概率不同。
二、为什么必须配套重写 equals 和 hashCode?
Java 官方约定两条通用规范:
- 如果两个对象通过equals()相等,那么两个对象的hashCode()返回值必须相等;
- 如果两个对象 hashCode 相等,equals 不一定相等(哈希碰撞)。
只重写 equals,不重写 hashCode 会发生什么?
两个业务上相等的对象,equals 返回 true,但 hashCode 基于内存地址生成,哈希值不一样。
当存入HashSet、HashMap时:
哈希表会先判断哈希值是否相同,哈希值不同会直接判定为两个不同对象,最终导致两个业务相同的对象都被存入集合,集合去重失效。
只重写 hashCode,不重写 equals 会发生什么?
两个对象哈希值相同,发生哈希碰撞后,会调用 equals 判断是否为同一个对象;如果使用原生 equals 对比内存地址,最终依旧判定为两个不同对象,同样无法实现去重。
三、结合 HashMap 底层详解失效场景
HashMap 存储键值对时的执行流程:
-
根据 key 的 hashCode 计算数组下标;
-
如果下标位置没有元素,直接存入;
-
如果下标已有元素(哈希碰撞),调用equals判断 key 是否完全相等;
-
equals 相等则覆盖旧 value,不相等则挂载到链表 / 红黑树。
举个例子:自定义 User 类,只重写 equals,根据 userId 判定相等,没有重写 hashCode:
-
user1 和 user2 的 userId 相同,equals 返回 true;
-
两个对象内存地址不同,原生 hashCode 返回不同数值;
-
put 进 HashMap 时,两个 key 会落在哈希表不同下标位置,最终两个相同业务 key 同时存入,出现重复 key。
四、正确重写规范
-
业务相等的属性同时用于 equals 判断和 hashCode 哈希计算;
-
推荐使用 IDE 自动生成 equals 和 hashCode 方法(基于业务唯一字段);
-
重写 equals 时一定要加@Override注解,避免方法签名写错。
面试高频总结
-
重写 equals 必须重写 hashCode,目的是保证相等对象哈希值一致,满足哈希表去重规则;
-
HashSet 底层基于 HashMap 实现,元素不可重复依赖 hashCode+equals 双重校验;
-
哈希碰撞:不同对象 hashCode 相同,此时需要 equals 做二次精准校验。

浙公网安备 33010602011771号