news 2026/1/26 15:15:00

hashCode 与 equals:面试官必问的核心关联,一篇讲透

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
hashCode 与 equals:面试官必问的核心关联,一篇讲透

文章目录

  • 前言
  • 一、先搞懂:hashCode 到底有什么用?
    • 1. 哈希码的本质:一个 “身份标识” 的简化版
    • 2. 核心应用场景:哈希集合的高效操作
    • 3. 关于 hashCode 的两个重要约定
  • 二、再理清:equals 方法的本职工作
    • 1. Object 类的默认实现
    • 2. 重写 equals 的正确姿势
  • 三、关键核心:为什么重写 equals 必须重写 hashCode?
    • 1. 反例:只重写 equals,不重写 hashCode
    • 2. 反例分析:违背了 hashCode 的等价性约定
    • 3. 正确做法:重写 equals 时,必须重写 hashCode
  • 四、总结:hashCode 与 equals 的核心关联
  • 五、面试高频坑点提醒

前言

大家好,我是程序员梁白开,今天我们聊一聊hashCode 与 equals。

在 Java 面试中,hashCodeequals绝对是高频考点,很多同学都能说出 “重写 equals 必须重写 hashCode”,但问到两者到底有什么用、为什么要强制绑定,却常常含糊其辞。今天就带大家从底层原理到实际应用,彻底搞懂这两个方法的 “爱恨情仇”。


一、先搞懂:hashCode 到底有什么用?

hashCode() 是 Java 中 Object 类的一个原生方法,它的核心作用是返回对象的哈希码值(int 类型),这个哈希码主要用于快速查找,是 HashMap、HashSet 等哈希集合的 “性能基石”。

1. 哈希码的本质:一个 “身份标识” 的简化版

你可以把哈希码理解为对象的一个 “简化指纹”。理论上,每个对象都有自己的内存地址(独一无二),但直接用内存地址作为标识进行查找,效率并不高。

hashCode() 会通过特定算法,将对象的内存地址或内部属性转化为一个 int 整数,这个整数就是哈希码,它的核心价值在于缩小查找范围

2. 核心应用场景:哈希集合的高效操作

我们以HashMap为例,看看 hashCode 是如何发挥作用的:

  1. 当向 HashMap 中 put 元素 时,会先计算 key 的 hashCode,根据 hashCode 直接定位到对应的哈希桶(数组下标)。
  2. 如果该哈希桶为空,直接将键值对存入;如果不为空,再通过 equals() 方法比较桶内元素与新 key 是否相等:
    • 相等则覆盖旧值;
    • 不相等则以链表或红黑树的形式挂载(解决哈希冲突)。
  3. 当从 HashMap 中 get 元素 时,同样先计算 key 的 hashCode,快速定位到哈希桶,再通过 equals() 精准匹配目标元素。

试想一下,如果没有 hashCode,每次查找都要遍历 HashMap 中的所有元素,时间复杂度会从 O (1) 退化到 O (n),在数据量大的场景下,性能差距会极其明显。

3. 关于 hashCode 的两个重要约定

根据 Java 官方文档,hashCode() 需要遵循以下通用约定,这是我们重写方法的准则:

  1. 一致性:在同一个 Java 程序执行期间,对同一个对象多次调用 hashCode(),必须返回相同的整数,前提是对象用于 equals() 比较的属性没有被修改。
  2. 等价性:如果两个对象通过 equals() 方法比较为相等,那么它们的 hashCode() 必须返回相同的整数。
  3. 非唯一性:如果两个对象通过 equals() 方法比较为不相等,它们的 hashCode() 可以相同(这就是哈希冲突),但建议不同,以提高哈希集合的性能。

二、再理清:equals 方法的本职工作

equals() 同样是 Object 类的原生方法,它的核心作用是 判断两个对象是否 “逻辑相等”。

1. Object 类的默认实现

Object 类中 equals() 的源码如下:

publicbooleanequals(Objectobj){return(this==obj);}

可以看到,默认的 equals() 本质上是 比较两个对象的内存地址,也就是判断两个引用是否指向同一个对象。

但在实际开发中,我们往往需要的是 “逻辑相等”。比如,对于一个 User 类,只要 id 相同,我们就认为两个 User 对象是相等的,这时候就需要 重写 equals() 方法。

2. 重写 equals 的正确姿势

以 User 类为例,重写 equals() 的规范写法:

publicclassUser{privateLongid;privateStringname;// 构造方法、getter/setter 省略@Overridepublicbooleanequals(Objecto){// 1. 自反性:自己和自己比较,返回 trueif(this==o)returntrue;// 2. 非空性 + 类型判断:避免空指针,且确保是同一类if(o==null||getClass()!=o.getClass())returnfalse;// 3. 类型强转,比较核心属性Useruser=(User)o;returnObjects.equals(id,user.id);// 用 Objects.equals 避免空指针}}

重写 equals() 时,要遵循 自反性、对称性、传递性、一致性 这四个原则,这里不再展开,感兴趣的同学可以查阅官方文档。

三、关键核心:为什么重写 equals 必须重写 hashCode?

这是面试的核心问题,我们用 反例 来理解这个强制要求的必要性。

1. 反例:只重写 equals,不重写 hashCode

假设我们只重写了 User 类的 equals() 方法(按 id 比较),但没有重写 hashCode(),此时 Object 类的默认 hashCode() 会根据对象内存地址生成哈希码。

publicstaticvoidmain(String[]args){Useru1=newUser(1L,"张三");Useru2=newUser(1L,"李四");// 因为 id 相同,equals 返回 trueSystem.out.println(u1.equals(u2));// true// 但默认 hashCode 基于内存地址,u1 和 u2 是不同对象,哈希码不同System.out.println(u1.hashCode());// 比如:123456System.out.println(u2.hashCode());// 比如:789012// 放入 HashSet 中HashSet<User>set=newHashSet<>();set.add(u1);set.add(u2);// 预期:因为 u1 和 u2 相等,set 中应该只有一个元素// 实际:set 中存在两个元素!System.out.println(set.size());// 输出 2}

2. 反例分析:违背了 hashCode 的等价性约定

上面的代码中,u1 和 u2 通过 equals() 比较为相等,但它们的 hashCode() 却不相同,这就 违背了 hashCode 的第二个约定。

当把这两个对象放入 HashSet 时:

  • 存入 u1:计算 u1 的 hashCode,定位到哈希桶 A,存入。
  • 存入 u2:计算 u2 的 hashCode,定位到哈希桶 B,存入。
  • 由于两个对象在不同的哈希桶中,HashSet 不会再调用 equals() 进行比较,最终导致两个 “相等” 的对象被同时存入集合,破坏了 HashSet 的 “元素唯一性” 特性。

3. 正确做法:重写 equals 时,必须重写 hashCode

我们为 User 类补充 hashCode() 的重写,保证相等的对象具有相同的哈希码:

@OverridepublicinthashCode(){// 基于 equals 中比较的核心属性 id 生成哈希码returnObjects.hash(id);}

此时再运行上面的测试代码:

  • u1.equals(u2) 为 true,u1.hashCode() 和 u2.hashCode() 也相同。
  • 存入 HashSet 时,u2 会定位到和 u1 相同的哈希桶,通过 equals() 比较后发现相等,不会被重复存入。
  • 最终 set.size() 输出 1,符合预期。

四、总结:hashCode 与 equals 的核心关联

维度hashCodeequals
核心作用生成对象哈希码,用于快速查找判断两个对象逻辑相等
调用时机哈希集合(HashMap/HashSet)添加、查询元素时优先调用哈希集合中定位到同一哈希桶后,用于精准匹配
关联规则相等的对象,hashCode 必须相同相同 hashCode 的对象,equals 不一定相等

一句话总结:
hashCode 是 “粗筛”,帮我们快速缩小查找范围;equals 是 “细筛”,帮我们精准判断对象是否相等。两者协同工作,才能保证哈希集合的高效与正确性。

五、面试高频坑点提醒

  1. 不要用随机数生成 hashCode:违反一致性约定,同一对象多次调用 hashCode 会返回不同值。
  2. 不要只重写 hashCode 而不重写 equals:没有意义,哈希集合依然无法正确判断元素唯一性。
  3. 重写 hashCode 时,要基于 equals 中的核心属性:比如 equals 比较 id 和 name,hashCode 也要包含这两个属性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/16 11:18:43

Kafka 消息不丢失全攻略:从生产到消费的全链路保障

Kafka 消息不丢失全攻略:从生产到消费的全链路保障 引言 Kafka 以其高吞吐和可靠性在分布式消息系统中广泛应用,但很多人以为 Kafka 默认就能“绝对不丢消息”。事实上,Kafka 的设计目标是高性能 + 可配置的可靠性,要做到真正的“不丢失”,需要从 生产者、Broker、消费者…

作者头像 李华
网站建设 2026/1/18 13:36:59

多线程循环打印123(个人题解golang版)

刷面筋看到这么一道手搓题&#xff0c;看网上题解大部分都是java的&#xff0c;就以我的理解用go实现了&#xff08;ai好笨啊&#xff0c;也可能是我不会用&#xff0c;问的全是错的最后还得自己手搓&#xff09;&#xff0c;纯古法手搓版&#xff1a;var num atomic.Int64 // …

作者头像 李华
网站建设 2026/1/16 11:37:58

comfyui + fluxGym角色固定工作流实战

FluxGym是什么 FluxGym 是一个专为 FLUX 模型设计的、极简化的 LoRA 训练工具。它的核心目的是让普通用户在消费级显卡&#xff08;如 12GB/16GB 显存&#xff09;上也能轻松LoRA&#xff0c;训练 AI 模型&#xff0c;无需面对复杂的参数设置&#xff0c;如果你想给 FLUX 炼制一…

作者头像 李华
网站建设 2026/1/15 23:23:07

特殊版解密神器,无限制,真好用!

APDFPR PDF解密软件 解压后&#xff0c;无需繁琐的安装步骤&#xff0c;直接点击对应图标即可打开使用。 首次使用时&#xff0c;建议先将软件界面设置为中文&#xff0c;这样操作起来会更加得心应手。 为了让大家更直观地感受它的强大功能&#xff0c;我们来做个小演示…

作者头像 李华
网站建设 2026/1/23 23:00:49

【专科生必看】查重率90%?AI痕迹99.8%?别慌!Paperzz三招教你3元搞定降重+降AIGC,导师都说“这孩子真会用工具”!

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿 https://www.paperzz.cc/weighthttps://www.paperzz.cc/weight 副标题&#xff1a; 专科论文不用熬通宵&#xff01;只需上传文档→选“智能降重”或“降AIGC”→等10分钟&#xff0c;重复率从90%降到8%&am…

作者头像 李华
网站建设 2026/1/21 9:34:24

DBO-LSTM预测模型:含注释、易替换数据的优化时间序列预测模型

DBO-LSTM预测模型&#xff0c;DBO优化LSTM的时间序列预测模型&#xff0c;有注释&#xff0c;替换数据就可以运行&#xff0c;全部自己写的&#xff0c;注释为中文&#xff0c;方便修改&#xff0c;有与基础版LSTM的对比结果图与误差对比图。 很适合同学们学习与绘图 最近在研…

作者头像 李华