为什么要重写hashCode和equals方法【深入分析版】

2023-11-08

 在回答这个问题前,我们先来看看Object类中的这两个方法:

public native int hashCode();
 
public boolean equals(Object obj) {
        return (this == obj);
    }

其中hashCode调用的是本地方法,如果子类补充下默认调用的是本地方法。Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。

equals方法比较的是内存地址是否相等,因此一般情况下也无法满足两个对象值的比较。

  1. 重写equals方法是为了比较两个不同对象的值是否相等
  2. 重写hashCode是为了让同一个Class对象的两个具有相同值的对象的Hash值相等。
  3. 同时重写hashCode()与equals()是为了满足HashSet、HashMap等此类集合的相同对象的不重复存储。

 

下面做个试验,用同一个Student类创建两个实例对象,然后比较各种情况下,这两个对象是否相等。

1、先将Student中的hashCode()方法注释掉 

package com.school.eution.accommodation;

import java.util.Objects;

public class Student {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student() {
        super();
    }
    public Student(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

  /*  @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }*/
}

进行如下测试: 

package com.school.eution.accommodation;
 
import java.util.HashMap;
import java.util.Map;
 
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student(1,"June");
        Student s2 = new Student(1,"June");

        Map<Object,String> testMap = new HashMap<Object,String>();
        testMap.put(s1, "美国学生");
        System.out.println(s1.hashCode());
        System.out.println(testMap.get(s1));

        System.out.println("------------------");
        System.out.println(s2.hashCode());
        System.out.println(testMap.get(s2));

        System.out.println("------------------");
        System.out.println(s1.equals(s2));
    }
}

 多次运行结果一致:

971848845
美国学生
------------------
1910163204
null
------------------
true

解释:如果不重写hashCode方法就会使用从Object继承来的本地hashCode()方法,所以将对象s1放入Map中后,通过s1对象能从Map中取出来字符串“美国学生”,但是s2和s1在值上是相等的,却不能使用s2对象从Map中也获取到同样的值。就是因为没有重写hashCode方法导致使用默认hasCode产生的Hash值不一致造成的。所以s2取值的时候按照s2的hash值“1910163204”就在Map中找不到。

但是如果不使用Map取值,直接通过equals比较是相等的(此时并没有重写hashCode)。

Map取值是先通过hash值定位,如果hash值存在于Map中,并且hash值一致就说明在Map的同一个链表中,继续使用equals方法比较值是否相等,如果hash值都不一致,就没必要往下比较了直接返回false。

下面将Student中重新hash的代码放开

package com.school.eution.accommodation;

import java.util.Objects;

public class Student {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Student() {
        super();
    }
    public Student(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }
}

再次运行结果:

2321474
美国学生
------------------
2321474
美国学生
------------------
true

因为重新了hashCode,此时,s1与s2的age与name值又是相等的,所以使用对象s1、s2都能从Map中取得相同的值。即使s2事先并没有向Map中存放,也就是可以使用s2与s1Hash值一样,来用s2获取s1存放到Map中的值。就好比s1与s2拿了同一把钥匙。

通过equals方法比较s1与s2依然是相等的。

 

总结:为什么一定要同时重写hashCode()方法和equals()方法,是针对HashSet和HashMap等这类使用hash值存储的对象而言的。比如:在向HashSet存放Student时,如果没有重写hashCode,这时往HashSet中存放s1、s2实际上是能存放两份的,即使s1与s2的值完全一致。但如果用不到这些Hash的集合,只重写equals()方法也能满足像个对象值是否相等的比较。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么要重写hashCode和equals方法【深入分析版】 的相关文章

  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 序列的排列?

    我有具体数量的数字 现在我想以某种方式显示这个序列的所有可能的排列 例如 如果数字数量为3 我想显示 0 0 0 0 0 1 0 0 2 0 1 0 0 1 1 0 1 2 0 2 0 0 2 1 0 2 2 1 0 0 1 0 1 1 0
  • Spring应用中Eureka健康检查的问题

    我正在开发一个基于 Spring 的应用程序 其中包含多个微服务 我的一个微服务充当尤里卡服务器 到目前为止一切正常 在我所有其他微服务中 用 EnableEurekaClient 我想启用这样的健康检查 应用程序 yml eureka c
  • 如何循环遍历所有组合,例如48 选择 5 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在java中从大小为n的集合中迭代生成k个元素子集 https stackoverflow com questions 4504974 how to iteratively generate k
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 如何更改javaFX中按钮的图像?

    我正在使用javaFX 我制作了一个按钮并为此设置了图像 代码是 Image playI new Image file c Users Farhad Desktop icons play2 jpg ImageView iv1 new Ima
  • Java 公历日历更改时区

    我正在尝试设置 HOUR OF DAY 字段并更改 GregorianCalendar 日期对象的时区 GregorianCalendar date new GregorianCalendar TimeZone getTimeZone GM
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • 如何访问JAR文件中的Maven资源? [复制]

    这个问题在这里已经有答案了 我有一个使用 Maven 构建的 Java 应用程序 我有一个资源文件夹com pkg resources 我需要从中访问文件 例如directory txt 我一直在查看各种教程和其他答案 但似乎没有一个对我有
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • MATLAB/simulink时域分析之性能指标(0基础)

    目录 6 时域分析 6 1 性能指标 6 1 1 典型输入信号 6 1 2 一阶系统时域响应 6 1 3 二阶系统时域响应 6 1 4 二阶系统的改善 6 时域分析 由于多数控制系统是以时间作为独立变量 所以人们往往关心输出对时间的响应 对
  • 刷脸支付创新高效促进消费者重新光临

    刷脸支付成为了移动金融产业新的焦点 这离不开代理的卖力推广 刷脸支付代理成为大多创业者的选择 从今往后 脸就是钱包走人寻常百姓家 再也不用担心发生突发情况 尴尬放回商品的局面 现在可以在便利店 部分夫妻店看见这样的画面 收银台不见了 换成了
  • python源码保护之cython

    转载请注明出处 准备 项目需要 是在windows7上操作 python3 7 针对python项目 而非单个的python程序 思路 先将py代码转成c代码 然后编译成pyd window上是pyd linux上是so 文件 安装cyth
  • base64转图片

    base64转图片 param base64Code base64码 public static void convertBase64ToImage String base64Code BufferedImage image byte im
  • Qt绘图与信号事件

    Qt应用开发的基本模式 面向对象 继承QDailog gkdialog h ifndef GK DIALOG H define GK DIALOG H include
  • 我的第一个python爬虫

    文章目录 前言 一 python爬虫是什么 二 豆瓣电影TOP250排行榜信息爬取 1 发送请求 2 获取数据 3 解析数据 4 保存数据 总结 前言 今天想跟大家分享下我完成第一个python爬虫项目的过程 同时记录自己的 第一次 我的第
  • windows下dll文件的创建详细教程

    1 前言 dll文件是啥 就不作过多赘述了 现在直接教大家如何创建与使用dll文件 本文基于windows系统 使用的编译相关工具为visual studio 2019 2 创建dll 2 1 创建dll工程 首先打开visual stud
  • LaTex使用技巧20:LaTex修改公式的编号和最后一行对齐

    写论文发现公式编号的格式不对 要求是如果是多行的公式 公式编号和公式的最后一行对齐 我原来使用的是 equation 环境 begin equation begin aligned a b c c d end aligned end equ
  • Unity中相机拍照并保存下来脚本

    以下是一个示例的Unity拍照脚本 用于拍摄相机看到的内容并保存在工程根目录下 using System using UnityEngine using System IO public class CameraCapture MonoBe
  • 八邻域断点检测

    八邻域断点检测 本文的理论思想主要来源大家可以参照 迈克老狼2012 OpenCV学习 13 细化算法 1 本文是我自己尝试着将八邻域的细化思想 运用到断点检测上 个人觉得其实仅仅是八邻域应用的一小方面大家可以尝试着往其他方面应用 其实相对
  • Linux SPI 总线 和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章 我们已经知道 整个SPI驱动架构可以分为协议驱动 通用接口层和控制器驱动三大部分 其中 控制器驱动负责最底层的数据收发工作 为了完成数据收发工作 控制器驱动需要完成以下这些功能 1 申请必要的硬件资源 例如中断 DMA通道
  • Photoshop提示暂存盘已满怎么办?ps暂存盘已满如何解决?

    打开ps软件提示暂存盘已满是什么意思 如何解决Photoshop提示暂存盘已满 本文给大家带来了解决办法 一起来看看吧 如果遇到 暂存盘已满 错误 通常意味着用作暂存盘的硬盘 或驱动器 用尽了执行任务所需的存储空间 解决方案 方法一 释放更
  • spring中的那一堆Configuration

    EnableAutoConfiguration 开启Spring Application Context自动配置 系统会根据你引入的jar包情况 自动配置一些需要的bean 参考spring boot autoconfigure jar 下
  • python典型案例:打印输出九九乘法表

    python典型案例 打印输出九九乘法表 使用for循环语句打印输出代码如下 for x in range 1 10 外循环控制行数 for y in range 1 x 1 内循环控制列 print x y x y end end表示 为
  • Styled-component 入门使用(一)

    Styled component 入门使用 一 styled components 优点 自动引入关键 styled components可以跟踪哪些组件在页面上呈现 并注入其样式 可以实现加载所需的最小代码 不会产生类名冲突 styled
  • 反向代理服务器:nginx

    1 什么是nginx Nginx engine x 是一个高性能的 HTTP 和 反向代理web服务器 其特点是占有内存少 并发能力强 事实上nginx的并发能力在同类型的网页服务器中表现较好 能够支持高达 50 000 个 并发连接数 的
  • LED灯条亮度色温调节

    一般LED灯条为12V或者24V供电 恒压驱动 由于LED灯条中已经内嵌了限流电阻 因此不需要使用复杂的恒流措施 对LED灯条进行色温和亮度的调节 可以通过PWM来开关MOS管 正白4000 4500K或者暖白3000 3500K 1 恒流
  • Linux常用命令(帮助命令、用户管理命令和压缩解压命令)

    详细目录 帮助命令 man whatis apropos help 用户管理命令 useradd passwd who w 压缩解压命令 gzip gunzip tar zip unzip bzip2 bunzip2 帮助命令 man 功能
  • 全球与中国移动健康传感器市场未来发展趋势及十四五投资战略规划研究报告2021-2027年版

    2020年 全球移动健康传感器市场规模达到了 亿元 预计2027年将达到 亿元 年复合增长率 CAGR 为 本报告研究全球与中国市场移动健康传感器的产能 产量 销量 销售额 价格及未来趋势 重点分析全球与中国市场的主要厂商产品特点 产品规格
  • 为什么要重写hashCode和equals方法【深入分析版】

    在回答这个问题前 我们先来看看Object类中的这两个方法 public native int hashCode public boolean equals Object obj return this obj 其中hashCode调用的是