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

智能推荐

基于图像的目标检测与定位方法概述_图像定位算法-程序员宅基地

文章浏览阅读1.4w次,点赞15次,收藏97次。目录1. 目标检测与定位概念2. 目标检测与定位方法2.1 传统目标检测流程2.2 two-stage检测算法2.2.1 R-CNN2.2.2 two-stage其他算法2.2.2.1 Spatial Pyramid Pooling(空间金字塔池化)2.2.2.2 Fast-RCNN2.2.2.3 P11 Faster RCNN2.3 One-Stage2.3.1 YOLO2.3.2 SSD参考本文简单介绍基于图像的目标检测与定位相关概念,R-CNN和YOLO等算法基本思想。本文为学习笔记,参考了许多优_图像定位算法

数字图像处理Matlab-小波变换在图像处理中的应用(附代码)_小波分解与重构在图像处理的应用-程序员宅基地

文章浏览阅读9.8k次,点赞40次,收藏131次。目录1.Objectives:2.Experiment Content:3.Experiment Principle:4.Experiment Steps Result and Conlusion:1、了解各种小波性质2、观察各种小波函数及其尺度函数3、获得小波滤波器4、二维小波变换函数与重构函数5、查看及处理小波系数6、用小波变换进行图像压缩与边缘提取【附录】实现代码1.Objectives:..._小波分解与重构在图像处理的应用

如何把海思 Hi3556、Hi3559当ipc芯片使用_hi3556的固件可以刷hi3559吗-程序员宅基地

文章浏览阅读8k次。解决思路:1、网络方面:这两颗料没有网口,那么给linux系统端移植一颗usb转网卡的芯片即可,价格一两块RMB,可行。2、去掉middleware和reference层,直接在ndk层进行操作;3、如果对图像有要求,可以在相关接口扩展指令,从而达到间接使用mpi接口的目的;..._hi3556的固件可以刷hi3559吗

创龙TI OMAP-L138(定点/浮点DSP C674x+ARM9)RJ45以太网口_am1808 代换-程序员宅基地

文章浏览阅读466次。CPUOMAP-L138、TMS320C6748、AM1808三款CPU管脚兼容,外设资源基本相同。OMAP-L138TI公司的达芬奇架构嵌入式应用处理器开始使用DSP与ARM结合的非对称多核结构,OMAP-L138就是其中的一款低功耗双核嵌入式处理器。OMAP-L138双核架构兼具DSP的高数字信号处理性能和精简指令计算机(RISC)技术的优点,双核均是32位处理器。以下是OMAP-L138 CPU的资源框图:RJ45以太网口开发板采用了SMSC的LAN8710A网卡芯片,它可以自_am1808 代换

个人测试文档_测试文档csdn-程序员宅基地

文章浏览阅读71次。YYYYYYYY欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新的写作体验;在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展_测试文档csdn

vue中的uri_Vue.js——vue-resource全攻略-程序员宅基地

文章浏览阅读201次。概述上一篇我们介绍了如何将$.ajax和Vue.js结合在一起使用,并实现了一个简单的跨域CURD示例。Vue.js是数据驱动的,这使得我们并不需要直接操作DOM,如果我们不需要使用jQuery的DOM选择器,就没有必要引入jQuery。vue-resource是Vue.js的一款插件,它可以通过XMLHttpRequest或JSONP发起请求并处理响应。也就是说,$.ajax能做的事情,vue-..._vue uri

随便推点

阿里云ESC-Ubuntu图形化界面安装(解决安装后依然只有控制台的问题)_阿里云ecs服务器ubantu只有命令行-程序员宅基地

文章浏览阅读1.7k次,点赞4次,收藏14次。刚买了阿里云服务器ESC S6共享型,操作系统选择了公共镜像中的Ubuntu16.04系统无需安装方便快捷,但安装的Ubuntu16.04系统是无图形化界面的,网上有大批大批的安装教程,但是我按照网上说法折腾了一下午都没能成功!!!终于在结合了多个博客所说的方法,我终于成功了,以下为我安装图形化界面并远程访问的流程:输入以下代码更新软件库apt-get update升..._阿里云ecs服务器ubantu只有命令行

零基础制作服务器,问下大佬们 零基础学搭建服务器要学些啥-程序员宅基地

文章浏览阅读855次。慕尼黑3412238先从java基础语法学起 然后是sql语法然后是JAVA 的servlt和 JDBC然后spring构建项目的时候需要用到点工具比如MAVEN 这个也很重要还需要一个web容器 tomcat最后如果你的服务器环境是linux还需要学习一下命令https://www.wandoujia.com/search?key=pcp%E9%87%8E%E7%8C%AB%E5%A8..._搭建服务器需要学什么

ROSTCM6情感分析结果乱码_rostcm6情感分析error-程序员宅基地

文章浏览阅读2.4w次,点赞2次,收藏12次。1.原因:出现这种乱码的原因是放入ROSTCM6文件编码不对,ROSTCM6放入的编码必须是ANSI,所以到处的时候把格式设置为ANSI就可以了。代码示例如下:import pandas as pdinputfile = '../data/meidi_jd_process_4.txt' #评论文件outputfile = '../data/meidi_jd_process_5.txt'......_rostcm6情感分析error

ubuntu实用指令_ubuntu命令大全-程序员宅基地

文章浏览阅读7.1k次,点赞3次,收藏68次。Ubuntu实用指令_ubuntu命令大全

原生 table 浏览器 border 粗细不一致(谷歌90版本以后) table兼容问题_兼容模式下 table表格边框变粗的问题-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏6次。.formTable{ border-spacing: 1px; background-color: rgba(201,201,201,1); /*边线颜色*/ border-collapse: separate; }td ,tr{ background-color: #fff;}谷歌更新到90版本后,border-collapse:collapse ; 属性出现问题导致,border 出现宽度不一![在这里插入图片描述](https://img-blog.csdni_兼容模式下 table表格边框变粗的问题

C调用汇编_c 里面调用汇编-程序员宅基地

文章浏览阅读1.6k次。1.80X86 32位汇编基础以及寄存器设定 2.栈帧与C函数调用 3.函数调用的汇编级解释以及栈图 4.stdcall和cdcel------------------------------------------------------1. 80X86 32位CPU的编程模型(programming model) 80X86有16个通用寄存器register。从某种程度_c 里面调用汇编