重写equlas时为什么一定要重写hashcode方法?

news2024/9/30 13:20:23

equals方法和hashCode方法都是Object类中的两个基本方法,它们共同来判断两个对象是否相等。为什么要两个方法结合起来使用呢?原因是在 ‘性能’ 上面。

使用过 hashMap 我们知道,通过 hash 计算 ,可以快速的在常量时间内找到某个值的存储位置。如果没有 hash 值我们的查找可能是通过遍历一个个询问比较找到这个对象再去比较值。显然这种遍历比较的方式效率比 hash 定位低。这就是 hash 和 hashCode 的存在意义。

 hashCode 寻址:(高效)

遍历寻址:(低效率) 

 

 所以在进行比较两个对象是否相等时候会先进行 hashCode 比较 hash 值是否相等,相等再进行 equals 比较内容是否相同,否则直接不同(hash 值相同对象不一定相同,hash 值不相同对象一定不相同)。以配合的方式来提交效率。

那为什么不直接使用 hashCode 就确定两个对象是否相等呢? ​

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

但即使知道了以上基础知识,依然解决不了本篇的问题,也就是:重写 equals 时为什么一定要重写 hashCode?要想了解这个问题的根本原因,我们还得先从这两个方法开始说起。

1.equals 方法


 Object 中的 equals 方用于检测两个对象的引用是否相同,如果引用相同那他们一定相同。

equals 方法源码实现:

 通过比较对象引用其实不具有参考价值。大多数比较的情况下,我们用来比较对象是否相同,这就完全没有意义(每一个对象的引用都不相同)

通过以下实例,可以说明这个问题:

public class equals {
    public static void main(String[] args) {

        Student s1 = new Student("极品小學生",18);
        Student s2 = new Student("极品小學生",18);

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

class Student {
    private String name;
    private int 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;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

 运行结果:

重写 equals 方法

    @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);
    }

 运行结果:

 因此,我们在判断两个对象是否相等时,一定要重写 equals 方法,这就是为什么要重写 equals 的原因。

2.hashCode 方法


 hashCode 中文为散列码,他是由对象推导出的一个整形值,这个值是随意的可能是任何一个值。

注意:散列码没有规律。如果x,y是两个不同的对象,x.hashCode()和y.hashCode()基本上不会相同(由于哈希值只是一个整数,它的范围是有限的),但是如果两个对象的值相同,那他们的hashCode 一定相同。

hashCode,源码实现:

 从源码中看出,Object 中的 hashCode 调用的本地方法(native),返回一个 int 类型整数。

相等的值 hashCode 一定相同的示例:

public class hashCodeDemo {
    public static void main(String[] args) {

        String s1 = "hello world";
        String s2 = "hello world";

        System.out.println("s1 hashCode: " + s1.hashCode());
        System.out.println("s2 hashCode: " + s2.hashCode());

    }
}

以上代码运行结果,如下图所示:


不同值的 hashCode 可能相同实例:

public class hashCodeDemo {
    public static void main(String[] args) {

        String s1 = "Aa";
        String s2 = "BB";

        System.out.println("s1 hashCode: " + s1.hashCode());
        System.out.println("s2 hashCode: " + s2.hashCode());

    }
}


3.为什么要一起重写?

1.为了提高查询效率,先通过 hashCode 比较再比较 equals 方法。

2.保证 Set 方法正常使用

Set 集合用来保存不同对象的,相同的对象就会被 Set 合并,保留下一份。

Set 正常使用演示:

public class hashCodeDemo {
    public static void main(String[] args) {

        Set<String> set = new HashSet<>();
        set.add("小红");
        set.add("小红");

        set.add("小明");
        set.add("小明");

        set.add("老六");
        System.out.println("Set 集合长度为: " + set.size());

        for (String s : set) {
            System.out.println(s);
        }
    }

以上代码执行结果:如下图所示:

 从实例中看出,重复的对象被 Set 集合合并,这也是 Set 集合最大特点: 去重

Set 集合 '异常'演示

然而,如果我们在 Set 集合中存储的是,只重写了 equals 方法的自定义对象时,有趣的事情就发生了,如下代码所示:

public class equals {
    public static void main(String[] args) {
        Set<Student> set = new HashSet<>();
        Student s1 = new Student("极品小學生",18);
        Student s2 = new Student("极品小學生",18);

        set.add(s1);
        set.add(s2);
        System.out.println("Set 集合长度为: " + set.size());

        for (Student s : set) {
            System.out.println(s);
        }
    }
}

class Student {
    private String name;
    private int age;

    @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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + 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;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

以上代码运行结果:如下图所示:

可以看出,两个相同内容的对象 被 Set 集合加入了两次并没有进行去重操作 。这样的结果显然不是我们想要的。首先我们要知道 Set 集合中,元素的唯一性是通过 equals 和 hashCode 方法共同来判断的。所以我们要重写 hashCode 方法。

重写了 hashCode 方法:

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

运行结果:

注意:Set 集合去重问题需要重写 equals 和 hashCode 两个方法,缺一种都是不可以的。

4.原因分析

在 Set 集合中,元素的唯一性是通过 equals 和 hashCode 方法共同来判断的。当往 Set 集合中添加一个元素时,首先会计算该元素的 hashCode 值,然后根据该值查找集合中是否已存在与该元素 hashCode 值相等的元素。

如果存在相等的 hashCode 值,则会继续调用它们的 equals 方法进行比较。如果两个元素的 equals 方法返回 true,那么就认为这两个元素相等,并且后来添加的元素将不被加入到集合中。

如果不存在相等的 hashCode 值,则认为当前元素与集合中的所有元素都不相等,直接将其加入到集合中。

因此,在 Set 集合中去重操作是基于 hashCode 和 equals 两个方法实现的,确保了集合中不会存在相同的元素。同时,为了保证正确性,我们需要在自定义对象的类中同时重写 equals 和 hashCode 方法,以便让它们能够正确地工作。

总结


hashCode 和 equals 方法 结合一起使用来提高判断对象是否相等的效率

因为 Set 集合通过 hashCode 和 equals 两个方法共同判断两个对象是否重复,所以要解决 Set 集合的异常,也需要重写这两种方法。

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

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

相关文章

webpack基本认知,它是什么,做什么的

一、基本概述 webpack本质是, 一个第三方模块包, 用于分析, 并打包代码 支持所有类型文件的打包支持less/sass > css支持ES6/7/8 > ES5压缩代码, 提高加载速度 二、安装 创建一个文件并运行以下命令&#xff1a; npm init -y npm i webpack webpack-cli -S 运行命令…

DNS域名解析服务

目录 一、DNS的简介 1&#xff09;DNS 数据结构分布 2&#xff09;服务器的类型 3&#xff09;DNS 域名解析方式 4&#xff09;DNS的查询方式 递归查询 迭代查询 二、DNS配置 1&#xff09;两台主从服务器进行配置操作 ​编辑 2&#xff09;DNS主域名服务器配置&am…

ITSS服务经理 、服务工程师线上开班在即

为了促进企业信息技术服务-运行维护服务能力&#xff0c;全面系统的提升员工的IT服务知识和技能水平&#xff0c;且更好的满足参训企业的时间需求&#xff0c;我司将于5月份开展ITSS服务经理、服务工程师线上班。 日期和形式 五月份&#xff1a;ITSS服务项目经理&#xff1a;…

Qlik Sense 集合表达式详解

文章目录 1 概述2 集合表达式 expression2.1 标识符 identifiers2.2 修饰符 modifiers2.2.1 多值用 &#xff0c;隔开2.2.2 引号区分大小写2.2.3 搜索 2.3 运算符 operators 3 应用 1 概述 #mermaid-svg-bQWKUrD934SlJaj9 {font-family:"trebuchet ms",verdana,arial…

电子招标采购系统源码:营造全面规范安全的电子招投标环境,促进招投标市场健康可持续发展

营造全面规范安全的电子招投标环境&#xff0c;促进招投标市场健康可持续发展 传统采购模式面临的挑战 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标…

CompletableFuture的基本使用和原理

CompletableFuture CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口&#xff0c;并在此基础上进行了丰富的扩展&#xff0c;完美弥补了Future的局限性&#xff0c;同时CompletableFuture实现了对任务编排的能力。借助这项能力&#xff0c;可以轻…

如何将matlab的m文件转换成python文件

因为matlab的内存实在太大了&#xff0c;所以我只在实验室电脑安装了matlab&#xff0c;自己电脑没有安装&#xff0c;现在跑实验需要把matlab文件转成python文件。在网上找到可以使用smop小工具。 我是在本地的anaconda转换的。先创建一个新环境到指定路径 conda create --pr…

HttpWebRequest类

HttpWebRequest类与HttpRequest类的区别。 HttpRequest类的对象用于服务器端&#xff0c;获取客户端传来的请求的信息&#xff0c;包括HTTP报文传送过来的所有信息。而HttpWebRequest用于客户端&#xff0c;拼接请求的HTTP报文并发送等。 HttpWebRequest这个类非常强大&#…

Spring MVC 接收 json 和返回 json (14)

目录 总入口 测试case 源码分析 1. 针对RequestBody的参数解析 2. 针对 ResponseBody 的返回值处理 总入口 通过上一篇Spring MVC 参数解析&#xff08;13&#xff09;_chen_yao_kerr的博客-CSDN博客的说明&#xff0c;相信大家对Sping MVC的参数解析有了一定的了解&…

2.微服务项目实战---环境搭建,实现电商中商品、订单、用户

使用的电商项目中的商品、订单、用户为案例进行讲解。 2.1 案例准备 2.1.1 技术选型 maven &#xff1a; 3.3.9 数据库&#xff1a; MySQL 5.7 持久层 : SpingData Jpa 其他 : SpringCloud Alibaba 技术栈 2.1.2 模块设计 springcloud-alibaba 父工程 shop-common …

【观察】构建“零信任”架构,筑起制造业安全“护城河”

中国是全球制造业大国&#xff0c;过去40年&#xff0c;中国制造业规模增长了18倍&#xff0c;其附加值达到2.2万亿美元&#xff0c;制造业在中国GDP比重高达40%&#xff0c;其之于中国经济的重要性可见一斑。 与此同时&#xff0c;中国制造业在高速发展的同时&#xff0c;也普…

使用全球融合CDN的10大优势

根据预估&#xff0c;今年的全球内容交付网络&#xff08;CDN&#xff09;市场预计将达到424亿美元。而由于移动应用程序的激增和人工智能尤其是ChatGPT等相关领域的快速发展将进一步带来CDN市场的快速增长&#xff0c;可以说全球CDN的黄金时代才刚开始。 融合CDN和多CDN战略是…

32道子网划分练习题详细解析含答案

目录 1 子网划分概念&#xff1a; 2 划分方法&#xff1a; 子网划分方法&#xff1a;段&#xff0c;块&#xff0c;数的计算三步。 段就是确定ip地址段中既有网络地址&#xff0c;又有主机地址的那一段是四段中的那一段&#xff1f; 块就确定上一步中确定的那一段中的主机…

企业云成本优化:减少企业云支出的终极指南

向云的转移使企业的技术领导者能够实现基础设施的现代化&#xff0c;并提高应用程序的可用性、可扩展性和性能。然而优化云成本对很多以互联网业务为主体的公司都是一项挑战&#xff0c;因为需要执行可持续的云成本管理战略。随着世界经济近年来走向低迷&#xff0c;尤其是互联…

【Linux网络服务】DNS域名解析服务服务

一、BIND域名服务基础 服务背景 1在日常生活中人们习惯使用域名访问服务器&#xff0c;但机器向互相只认IP地址&#xff0c;域名与IP地址之间是多对一的关系&#xff0c;一个IP址不一定只对应一个域名&#xff0c;且一个完成域名只可以对应一个IP地址&#xff0c;它们之间转换…

[ARM+Linux] 基于wiringPi库的串口通信

wiringOP-master/examples/serialTest.c中&#xff0c;wiringPi库中自带的串口程序&#xff1a; #include <stdio.h> #include <string.h> #include <errno.h>#include <wiringPi.h> #include <wiringSerial.h>int main () {int fd ;int count …

JavaSE-part1

文章目录 Day01 面向对象特性1.java继承注意点2.多态2.1多态概述2.2多态中成员的特点:star::star:2.3多态的转型:star::star: 3.Super4.方法重写:star::star:5.Object类:star::star: Day02 面向对象特性1.代码块:star:(主要是初始化变量&#xff0c;先于构造器)2.单例设计模式:…

服务器初始化

Linux基础系类 提示&#xff1a;个人学习总结&#xff0c;仅供参考。 一、Linux系统部署 二、服务器初始化 提示&#xff1a;文档陆续更新整理 服务器初始化 Linux基础系类简介一、配置IP地址二、配置YUM源&#xff08;yum本地源和yum网络源&#xff09;1.简介2.准备工作3.配置…

数据结构与算法——深度寻路算法

&#x1f4d6;作者介绍&#xff1a;22级树莓人&#xff08;计算机专业&#xff09;&#xff0c;热爱编程&#xff1c;目前在c&#xff0b;&#xff0b;阶段&#xff0c;因为最近参加新星计划算法赛道(白佬)&#xff0c;所以加快了脚步&#xff0c;果然急迫感会增加动力>——…

SQL Server的行级安全性

行级安全性 一、前言二、描述三、权限四、安全说明&#xff1a;侧信道攻击五、跨功能兼容性六、示例 一、前言 行级别安全性使您能够使用组成员身份或执行上下文来控制对数据库表中行的访问。 行级别安全性 &#xff08;RLS&#xff09; 简化了应用程序中的安全性设计和编码。…