equals 和 hashCode 到底有什么联系?一文告诉你!_hashcode equals关系-程序员宅基地

技术标签: Java  

写在前面

Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码。equals()和hashCode()都不是final方法,都可以被重写(overwrite)。

本文介绍了2种方法在使用和重写时,一些需要注意的问题。

equal()方法

Object类中equals()方法实现如下:

通过该实现可以看出,Object类的实现采用了区分度最高的算法,即只要两个对象不是同一个对象,那么equals()一定返回false。

虽然我们在定义类时,可以重写equals()方法,但是有一些注意事项;JDK中说明了实现equals()方法应该遵守的约定:

1)自反性:x.equals(x)必须返回true。

2)对称性:x.equals(y)与y.equals(x)的返回值必须相等。

3)传递性:x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)必须为true。

4)一致性:如果对象x和y在equals()中使用的信息都没有改变,那么x.equals(y)值始终不变。

5)非null:x不是null,y为null,则x.equals(y)必须为false。

hashCode()方法

1

Object的hashCode()

可以看出,hashCode()是一个native方法,而且返回值类型是整形;实际上,该native方法将对象在内存中的地址作为哈希码返回,可以保证不同对象的返回值不同。

与equals()方法类似,hashCode()方法可以被重写。JDK中对hashCode()方法的作用,以及实现时的注意事项做了说明:

1)hashCode()在哈希表中起作用,如java.util.HashMap。

2)如果对象在equals()中使用的信息都没有改变,那么hashCode()值始终不变。

3)如果两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等。

4)如果两个对象使用equals()方法判断为不相等,则不要求hashCode()也必须不相等;但是开发人员应该认识到,不相等的对象产生不相同的hashCode可以提高哈希表的性能。

2

hashCode()的作用

总的来说,hashCode()在哈希表中起作用,如HashSet、HashMap等。

当我们向哈希表(如HashSet、HashMap等)中添加对象object时,首先调用hashCode()方法计算object的哈希码,通过哈希码可以直接定位object在哈希表中的位置(一般是哈希码对哈希表大小取余)。如果该位置没有对象,可以直接将object插入该位置;如果该位置有对象(可能有多个,通过链表实现),则调用equals()方法比较这些对象与object是否相等,如果相等,则不需要保存object;如果不相等,则将该对象加入到链表中。

这也就解释了为什么equals()相等,则hashCode()必须相等。如果两个对象equals()相等,则它们在哈希表(如HashSet、HashMap等)中只应该出现一次;如果hashCode()不相等,那么它们会被散列到哈希表的不同位置,哈希表中出现了不止一次。

实际上,在JVM中,加载的对象在内存中包括三部分:对象头、实例数据、填充。其中,对象头包括指向对象所属类型的指针和MarkWord,而MarkWord中除了包含对象的GC分代年龄信息、加锁状态信息外,还包括了对象的hashcode;对象实例数据是对象真正存储的有效信息;填充部分仅起到占位符的作用, 原因是HotSpot要求对象起始地址必须是8字节的整数倍。可以点击此处查看更多解析JVM内存解析。

String中equals()和hashCode()的实现

String类中相关实现代码如下:

通过代码可以看出以下几点:

1、String的数据是final的,即一个String对象一旦创建,便不能修改;形如String s = “hello”; s = “world”;的语句,当s = “world”执行时,并不是字符串对象的值变为了”world”,而是新建了一个String对象,s引用指向了新对象。

2、String类将hashCode()的结果缓存为hash值,提高性能。

3、String对象equals()相等的条件是二者同为String对象,长度相同,且字符串值完全相同;不要求二者是同一个对象。

4、String的hashCode()计算公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

关于hashCode()计算过程中,为什么使用了数字31,主要有以下原因:

1)使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。

2)使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。

3)JVM会自动对31进行优化:31 * i == (i << 5) – i

如何重写hashCode()

本节先介绍重写hashCode()方法应该遵守的原则,再介绍通用的hashCode()重写方法。

1

重写hashcode()的原则

通过前面的描述我们知道,重写hashCode需要遵守以下原则:

1)如果重写了equals()方法,检查条件“两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等”是否成立,如果不成立,则重写hashCode ()方法。

2)hashCode()方法不能太过简单,否则哈希冲突过多。

3)hashCode()方法不能太过复杂,否则计算复杂度过高,影响性能。

2

hashCode()重写方法

《Effective Java》中提出了一种简单通用的hashCode算法

A、初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;


B、选取equals方法中用于比较的所有域(之所以只选择equals()中使用的域,是为了保证上述原则的第1条),然后针对每个域的属性进行计算:

1) 如果是boolean值,则计算f ? 1:0;
2) 如果是byte\char\short\int,则计算(int)f;
3) 如果是long值,则计算(int)(f ^ (f >>> 32));
4) 如果是float值,则计算Float.floatToIntBits(f);
5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int;
6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0;
7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。

C、最后,把每个域的散列码合并到对象的哈希码中。 

下面通过一个例子进行说明。在该例中,Person类重写了equals()方法和hashCode()方法。因为equals()方法中只使用了name域和age域,所以hashCode()方法中,也只计算name域和age域。

对于String类型的name域,直接使用了String的hashCode()方法;对于int类型的age域,直接用其值作为该域的hash。

微信公众号:javafirst

扫码关注免费获取更多资源 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiamiflying/article/details/81476979

智能推荐

markdown插入图片_markdom 插图-程序员宅基地

文章浏览阅读83次。markdown插入图片注意事项在markdown插入图片,光标要放在最前面,要不插入的图片不显示_markdom 插图

[WinError 127] 找不到指定的程序。 Error loading “D:\A\lib\site-packages\torch\lib\c10_cuda.dill_error loading "d:\anaconda\lib\site-packages\torch-程序员宅基地

文章浏览阅读7k次,点赞5次,收藏9次。可以参考一下几种解决方法:1、可能由于缺少VC++ Redistributable,在错误中会提示你下载并且提供了下载地址,只需要下载并默认安装即可;2、方法1如果不行,可以更新numpy;参考这个连接3、或者使用pip install intel-openmp,试试,参考这个连接。..._error loading "d:\anaconda\lib\site-packages\torch\lib\c10_cuda.dll" or one

BIOS的基本概念和理解_bios硬件的软件抽象-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏11次。1.基本概念BIOS(Basic Input/Output System)基本输入输出系统。是一种业界标准的固件接口;BIOS是个人电脑启动时候加载的第一个软件;BIOS用于电脑开机时,运行系统各部分的自我检测,并加载引导程序或存储在主存上的作业系统;BIOS向作业系统提供一些系统参数;系统硬件的变化是由BIOS隐藏,程序使用BIOS功能而不是直接控制硬件。现代作业系统会忽_bios硬件的软件抽象

Rockchip之RK3288HDMI接口插拔检测_hpd脚-程序员宅基地

文章浏览阅读5.3k次,点赞11次,收藏29次。Rockchip之RK3288HDMI接口插拔检测任务背景:最近机器的一块屏出现不显示或者白屏现象,这块屏是一块MIPI屏,但它是由3288上的HDMI接口通过一块LPC转接板转成MIPI接口的,所以根源还是HDMI接口,猜想可能是HDMI转MIPI的HDMI插拔检测脚导致的,因此,试着把这个插入检测去掉让HDMI信号直接输出看看结果,首先查看原理图检测脚为HDMI_HPD这个脚,首先介绍..._hpd脚

使用PyQt的QTableWidget来创建表格,并通过设置委托实现选中某个单元格时的背景颜色变化是一种常见的需求。在本文中,我将为您提供详细的代码示例和解释。_pyqt5 tablewidget 设置单元格颜色-程序员宅基地

文章浏览阅读203次。使用PyQt的QTableWidget来创建表格,并通过设置委托实现选中某个单元格时的背景颜色变化是一种常见的需求。在本文中,我将为您提供详细的代码示例和解释。接下来,我们使用双重循环创建了一些示例数据,并将其设置为表格的单元格项。最后,我们将表格设置为主窗口的中央部件,并显示主窗口。接下来,我们将创建一个简单的PyQt应用程序,并在其中添加一个QTableWidget。在上述代码中,我们首先导入了所需的类和模块。方法中,我们检查选中状态,并根据需要设置单元格的背景颜色。实例,并设置了行数和列数。_pyqt5 tablewidget 设置单元格颜色

DPDK — 安装部署(Ubuntu 18.04)_dpdk安装部署-程序员宅基地

文章浏览阅读1.5k次,点赞2次,收藏2次。目录文章目录目录环境参数环境依赖准备安装 DPDK测试附 1:Enable pcap环境参数Intel x86Ubuntu 18.04 LTSPython 3.6DPDK 18.08NICs virtio controller环境依赖准备# 自动解决必要依赖包安装的工具sudo apt-get install build-essential# 更新系统sudo apt-get update -y && sudo apt-get upgrade -y# Kerne_dpdk安装部署

随便推点

文献检索与应用_试述常用的二种医学专业搜索引擎的特色与检索方法?-程序员宅基地

文章浏览阅读3.8k次。信息检索的方法有多种,分别使用于不同的检索目的和检索要求。归纳起来,常用的信息检索方法有常规检索法、回溯检索法、循环检索法。1.常规检索法。又称常用检索法、工具检索法。它以主题、分类、作者等为检索点,利用检索工具获的信息资源的方法。根据检索方式,常规检索法又分为直接检索法和间接检索法;根据检索需求,常规检索法又分为顺查法、倒查法和抽查法。_试述常用的二种医学专业搜索引擎的特色与检索方法?

知识图谱基本概念&工程落地常见问题-程序员宅基地

文章浏览阅读1.1k次。作者:cavities来源:https://zhuanlan.zhihu.com/p/62824358编辑:happyGirl简要说明一下,搞了知识图谱架构一年半,快两年的一些小心得,后..._图谱能做到哪些知关系据库做不到的

关于unity中的update、Lateupdate和FixedUpdate。_unitylateupdate-程序员宅基地

文章浏览阅读1k次。MonoBehaviour.Update 更新 当MonoBehaviour启用时,其Update在每一帧被调用。 MonoBehaviour.FixedUpdate固定更新 当MonoBehaviour启用时,其 FixedUpdate在每一帧被调用。 处理Rigidbody时,需要用FixedUpdate代替Upd_unitylateupdate

PL/SQL Developer连接本地Oracle 11g 64位数据库_oracle11gr2 plsql dev-程序员宅基地

文章浏览阅读283次。登陆PL/SQL假定本地电脑中已经安装了Oracle 11gR2数据库和PL/SQL developer。如果没有安装可以在一下地址下载安装:Oracle 11gR2数据库:https://www.oracle.com/technetwork/database/enterprise-edition/downloads/112010-win64soft-094461.htmlPL/SQL developer(含注册机):https://pan.baidu.com/s/1kUfY8GB 密码: 1ky8_oracle11gr2 plsql dev

HDOJ1015-Safecracker优化版_safecracker hdoj-程序员宅基地

文章浏览阅读125次。纯暴力搜索是可以过的。但要求答案是最大的字典序,显然先排序后搜索可以一遍求出答案后就返回。但新手写回溯会出种种bug,最经典莫过于不知道如何返回而陷入死循环。这里给出一个参考代码:#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<map>#include<queue>#include<string>_safecracker hdoj

蓝桥试题 算法提高 书院主持人 JAVA_蓝桥杯试题 算法提高 书院主持人java-程序员宅基地

文章浏览阅读4.5k次。问题描述  北大附中书院有m个同学,他们每次都很民主地决策很多事情。按罗伯特议事规则,需要一个主持人。同学们民主意识强,积极性高,都想做主持人,当然主持人只有一人。为了选出主持人,他们想到了一个办法并认为很民主。方法是:  大家围成一圈,从1到m为每个同学编号。然后从1开始报数, 数到n的出局。剩下的同学从下位开始再从1开始报数。最后剩下来的就是主持人了。现在已经把同学从1到m编号,并约定报数..._蓝桥杯试题 算法提高 书院主持人java

推荐文章

热门文章

相关标签