news 2026/2/13 9:23:11

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/2/12 20:16:18

58、Ubuntu实用工具、测试参与及Perl编程入门指南

Ubuntu实用工具、测试参与及Perl编程入门指南 在Ubuntu系统的使用和开发过程中,有许多实用的工具和方法,同时也有多种途径可以参与到Ubuntu社区的建设中。此外,Perl作为一种强大的脚本语言,在Ubuntu系统中也有着广泛的应用。下面将为大家详细介绍这些内容。 实用工具介绍…

作者头像 李华
网站建设 2026/2/8 1:29:05

HTML奇妙冒险第一关:从零开始的网页构建之旅

本文将主要梳理html网页布局的基础知识与常用标签以及注意事项&#xff0c;对于软件的选用与课外知识不加涉猎 文章目录一、HTML的基本结构标签二、常用的HTML标签与必要的基本知识点1、基础知识之标签结构说明&#xff1a;2、基础知识之属性特性说明&#xff1a;3、常用标签1.…

作者头像 李华
网站建设 2026/2/10 6:46:02

QGIS open sans font安装失败

设置一下就可以了 打开QGIS&#xff0c;选择设置选择选项选择 字体上图红框勾选去掉即可

作者头像 李华
网站建设 2026/2/7 20:20:09

【单片机毕业设计】【mcugc-mcu912】基于单片机的智能饮水机

一、基本介绍 功能&#xff1a; 1、通过一个按键模拟加热电源的开关 2、通过防水式DS18B20检测水温&#xff0c;当电源打开时&#xff0c;温度小于设置最小值&#xff0c;进行自动加热&#xff0c;直到加热到最大值时停止&#xff1b; 3、通过三个LED灯显示当前状态&#xff0c…

作者头像 李华
网站建设 2026/2/11 19:27:39

JoyAgent-JDGenie系统架构设计

系统架构设计 📌 查看流程图说明 本文档使用 Mermaid 语法绘制流程图。如果流程图无法正常显示,请使用以下方式查看: VS Code: 安装 “Markdown Preview Mermaid Support” 扩展 GitHub/GitLab: 直接查看,自动支持 Mermaid 在线编辑器: 访问 Mermaid Live Editor 复制代码…

作者头像 李华