Java面试题总结-hashcode和equals

news2025/1/20 19:15:26

前段时间有朋友问我:“你重写过 hashcodeequals 么,为什么重写 equals 时必须重写 hashCode 方法?”

之前的学习中有深入了解过,后来很久没复习了,淡忘许多,回答的时候也有很多地方卡壳,干脆就总结一下这方面的知识点,也方便以后查看复习。
hashCode()介绍

首先先介绍一下hashCode()hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义 在 JDK 的 Object.java 中,这就意味着
Java 中的任何类都包含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有hashCode()

我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置。(摘自我的 Java 启蒙书《Headfirst java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

equals方法介绍

Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。在 Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。​

equals 方法的实现源码如下:

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

通常情况下,我们要判断两个对象是否相等,一定要重写 equals 方法,这就是为什么要重写 equals 方法的原因。
介绍完hashCode方法以及equals方法后,那么为什么要设计成hashCode方法和equals方法共同协作来判断两个对象是否相等?原因就出在“性能”二字上!!

当我们对比两个对象是否相等时,我们就可以先使用 hashCode 进行比较,如果比较的结果是 true,那么就可以使用 equals再次确认两个对象是否相等,如果比较的结果是true,那么这两个对象就是相等的,否则其他情况就认为两个对象不相等。这样就大大的提升了对象比较的效率,这也是为什么 Java 设计使用hashCodeequals 协同的方式,来确认两个对象是否相等的原因。​

能不能直接使用hashCode方法就确定两个对象是否相等呢?

答:不能,这是因为不同对象的 hashCode 可能相同;但 hashCode 不同的对象一定不相等,所以使用 hashCode可以起到快速初次判断对象是否相等的作用。​

接下来回答我们最开始的问题。

为什么要一起重写?
我们来看一个例子:Set 集合的“异常”(我们都知道Set具有去重的作用)

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class EqualsExample {
    public static void main(String[] args) {
        // 对象 1
        Persion p1 = new Persion();
        p1.setName("Java");
        p1.setAge(18);
		// 对象 2
        Persion p2 = new Persion();
        p2.setName("Java");
        p2.setAge(18);
		// 创建 Set 集合
        Set<Persion> set = new HashSet<Persion>();
        set.add(p1);
        set.add(p2);
		// 打印 Set 中的所有数据
        set.forEach(p -> {
            System.out.println(p);
        });
    }
}


class Persion {
    private String name;
    private int age;

    // 只重写了 equals 方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // 引用相等返回 true
        // 如果等于 null,或者对象类型不同返回 false
        if (o == null || getClass() != o.getClass()) return false;
        // 强转为自定义 Persion 类型
        Persion persion = (Persion) o;
        // 如果 age 和 name 都相等,就返回 true
        return age == persion.age &&
                Objects.equals(name, persion.name);
    }
	
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
     @Override
    public String toString() {
        return "Persion{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

以上程序的执行结果,如下图所示:
在这里插入图片描述
从上述代码和上述图片可以看出,即使两个对象是相等的,Set 集合竟然没有将二者进行去重与合并。这就是重写了 equals 方法,但没有重写 hashCode 方法的问题所在。

为了尝试解决这个问题,我们试着来把hashCode方法也重写,实现代码如下:

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class EqualsToListExample {
    public static void main(String[] args) {
        // 对象 1
        Persion p1 = new Persion();
        p1.setName("Java");
        p1.setAge(18);
		// 对象 2
        Persion p2 = new Persion();
        p2.setName("Java");
        p2.setAge(18);
		// 创建 Set 对象
        Set<Persion> set = new HashSet<Persion>();
        set.add(p1);
        set.add(p2);
		// 打印 Set 中的所有数据
        set.forEach(p -> {
            System.out.println(p);
        });
    }
}


class Persion {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // 引用相等返回 true
        // 如果等于 null,或者对象类型不同返回 false
        if (o == null || getClass() != o.getClass()) return false;
        // 强转为自定义 Persion 类型
        Persion persion = (Persion) o;
        // 如果 age 和 name 都相等,就返回 true
        return age == persion.age &&
                Objects.equals(name, persion.name);
    }

    @Override
    public int hashCode() {
        // 对比 name 和 age 是否相等
        return Objects.hash(name, age);
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Persion{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

以上程序的执行结果,如下图所示:
在这里插入图片描述
通过上述结果可以看出,当我们一起重写了两个方法之后,奇迹的事情又发生了,Set 集合又恢复正常了,这是为什么呢?

原因分析

出现以上问题的原因是,如果只重写了 equals 方法,那么默认情况下,Set 进行去重操作时,会先判断两个对象的 hashCode 是否相同,此时因为没有重写 hashCode 方法,所以会直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是两个不同引用地址的对象,所以结果是 false,那么 equals 方法就不用执行了,直接返回的结果就是 false:两个对象不是相等的,于是就在 Set 集合中插入了两个相同的对象。​
但是,如果在重写 equals 方法时,也重写了 hashCode 方法,那么在执行判断时会去执行重写的 hashCode 方法,此时对比的是两个对象的所有属性的 hashCode 是否相同,于是调用hashCode 返回的结果就是 true,再去调用 equals 方法,发现两个对象确实是相等的,于是就返回 true 了,因此 Set 集合就不会存储两个一模一样的数据了,于是整个程序的执行就正常了。

最后总结

hashCodeequals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,如果在重写 equals 时,不重写 hashCode,就会导致在某些场景下,例如将两个相等的自定义对象存储在 Set 集合时,就会出现程序执行的异常,为了保证程序的正常执行,所以我们就需要在重写 equals 时,也一并重写 hashCode 方法才行。

hashCode()与 equals()的相关规定

  1. 如果两个对象相等,则 hashcode 一定也是相同的
  2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  3. 两个对象有相同的 hashcode 值,它们也不一定是相等的
  4. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  5. hashCode()默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两 个对象无论如何都不会相等(即使这两个对象指向相同的数据)

最后感谢一下我的老师磊哥,很多地方老师上课都讲过,很久没用就老忘记,最后不得不翻阅老师的笔记之后才总结出来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/92929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构Java版】Queue队列的活用

目录 一、队列的定义 二、队列的使用 &#xff08;1&#xff09;主要方法 &#xff08;2&#xff09;实例演示 ​&#xff08;3&#xff09;注意事项 三、队列的模拟实现 四、循环队列 &#xff08;1&#xff09;循环队列定义 ​&#xff08;2&#xff09;循环队列的表…

web前端期末大作业:美食文化网页设计与实现——美食餐厅三级(HTML+CSS+JavaScript)

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

Cambridge IGCSE Mathematics真题讲解1

考试局&#xff1a;Cambridge Assessment International Education (CAIE)考试类别&#xff1a;Cambridge International General Certificate of Secondary Education (IGCSE)考试科目&#xff1a;Mathematics考试单元&#xff1a;Paper 2 (Extended)试卷代码&#xff1a;0580…

全栈Jmeter接口测试(十四):跨线程组传递jmeter变量及cookie的处理

setUp线程组 setUp thread group&#xff1a; 一种特殊类型的线程组&#xff0c;用于在执行常规线程组之前执行一些必要的操作。 在 setup线程组下提到的线程行为与普通线程组完全相同。不同的是执行顺序--- 它会在普通线程组执行之前被触发&#xff1b; 应用场景举例&#xf…

大二Web课程设计:服装网页设计题材——HTML+CSS汉服文化带背景音乐素材带视频(12页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

SQLAlchemy

一 概述 SQLAlchemy是 SQL工具包和对象关系映射器用于使用 数据库和 Python。它有几个不同的区域 &#xff0c;可单独使用或组合使用。其主要组成部分如下所示&#xff0c; 将组件依赖项组织成层&#xff1a; 上面两个最重要的部分 SQLAlchemy是对象关系映射器&#xff08;OR…

联盟营销是什么?和网红营销有什么区别?

之前讲过一篇关于联盟营销文章相关的&#xff0c;发现大家都很感兴趣&#xff0c;今天东哥就专门写一篇更全面的文章给大家好好介绍一下联盟营销以及它跟网红营销有什么区别吗&#xff1f; 联盟营销是什么&#xff1f; 联盟营销是一种根据营销效果付费的营销模式。商家利用第三…

Flink 运行错误 java.lang.OutOfMemoryError: Direct buffer memory

如遇到如下错误&#xff0c;表示需要调大配置项 taskmanager.memory.framework.off-heap.size 的值&#xff0c;taskmanager.memory.framework.off-heap.size 的默认值为 128MB&#xff0c;错误显示不够用需要调大。 2022-12-16 09:09:21,633 INFO [464321] [org.apache.hadoo…

西门子Siemens EDI需求分析及解决方案

西门子股份公司是一家专注于工业、基础设施、交通和医疗领域的科技公司&#xff0c;始终致力于做到订单、供应以及财务流程的安全、经济、高效&#xff0c;并努力提高自身与交易伙伴之间电子商务的互惠互利。为了提高与交易伙伴之间的数据传输效率&#xff0c;西门子Siemens ED…

1571_AURIX_TC275_ERU寄存器以及锁步控制

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 这些寄存器bits其实是对应了MCU的信号路由设计。 FC其实是flag clear的一个缩写&#xff0c;这样可以明确弄清楚前面文字的描述。对应的&#xff0c;FS&#xff0c;其实是flag set。下面的…

加解密与HTTPS(1)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 网络安全是最近几年越来越被社会和国家高层关注的问题&#xff0c;比如米国网络部队、棱镜门、乌云网事件、摄像头偷拍等。武汉在2019年就建成了全国最大也是唯一…

Nature论文:VR中OLED和LCD的时空图像质量探究

VR头显对空间分辨率和响应时间的要求很高&#xff0c;然而&#xff0c;在VR头显移动时&#xff0c;还没有一种可以在时空域中量化VR图像质量的标准方法。近期在一项新研究中&#xff0c;科研人员测试了三款VR头显&#xff08;HTC Vive、Vive Pro、Vive Pro 2&#xff09;在平滑…

微信公众号开发——实现用户微信网页授权流程

&#x1f60a; 作者&#xff1a; 一恍过去&#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390&#x1f38a; 社区&#xff1a; Java技术栈交流&#x1f389; 主题&#xff1a; 微信公众号开发——实现用户微信网页授权流程⏱️ 创作时间&#xff1a; …

阿里云效产品【代码管理Codeup】企业项目代码管理

文章目录前言一、Codeup是什么二、使用步骤1.首先登录阿里云2.进入云效3.进入云效4.代码分组5.新建代码库三、SSH 密钥总结前言 阿里云Code&#xff08;新版&#xff1a;代码托管Codeup&#xff09;阿里云代码管理 Codeup是基于 Git 的代码管理平台&#xff0c;10万企业正在使…

【头歌C语言程序与设计】顺序结构程序设计

目录 写在前面 正文 第1关&#xff1a;加法运算 第2关&#xff1a;不使用第3个变量&#xff0c;实现两个数的对调 第3关&#xff1a;用宏定义常量 第4关&#xff1a;数字分离 第5关&#xff1a;计算总成绩和平均成绩 第6关&#xff1a;求三角形的面积 第7关&#xff1…

黑客入门指南,学习黑客必须掌握的技术

黑客一词&#xff0c;原指热心于计算机技术&#xff0c;水平高超的电脑专家&#xff0c;尤其是程序设计人员。是一个喜欢用智力通过创造性方法来挑战脑力极限的人&#xff0c;特别是他们所感兴趣的领域&#xff0c;例如电脑编程等等。 提起黑客&#xff0c;总是那么神秘莫测。…

CentOS7安装MySQL

CentOS7安装MySQL 在CentOS中默认安装有MariaDB&#xff0c;这个是MySQL的分支&#xff0c;但为了需要&#xff0c;还是要在系统中安装MySQL&#xff0c;而且安装完成之后可以直接覆盖掉MariaDB。 下载并安装MySQL官方的 Yum Repository ​[rootlocalhost ~]# wget -i -c ht…

Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器

Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器。 短信转发器——不仅只转发短信&#xff0c;备用机必备神器&#xff01; 监控Android手机短信、来电、APP通知&#xff0c;并根据指定规则转发到其他手机&#xff1a;钉钉群自定义机器人、钉钉企业内机器人、企业微信…

c#入门-接口显式实现

接口显式实现 接口的显式实现主要解决两个问题 基类型隐式实现了一个接口成员。但是他的成员没有标记虚拟的&#xff0c;无法重写。接口可以多继承&#xff0c;那么重名了怎么办 显式继承语法 interface I回血 {public void 回血(); }显式继承时&#xff0c;不能写访问修饰…

团队新人多,稳定性经验不足,研发质量怎么保障?|TakinTalks论道

# 一分钟精华速览 #在研发和稳定性保障过程中&#xff0c;人与设备、程序、组织的交互是一个复杂的过程&#xff0c;虽然人们极少会恶意犯错&#xff0c;但由于受特定情景下的实际条件影响&#xff0c;人为失误也时有发生&#xff0c;那么&#xff0c;如何尽可能减少这些失误的…