为什么要重写hashCode()方法和equals()方法以及如何进行重写

一、前言

本篇文章主要探讨的问题有三个:

1、首先我们为什么需要重写hashCode()方法和equals()方法?
2、在什么情况下需要重写hashCode()方法和equals()方法?
3、如何重写这两个方法?

二、为什么需要重写hashCode()方法和equals()方法

首先,为什么要重写equals()方法。我们在定义类时,我们经常会希望两个不同对象的某些属性值相同时就认为他们相同,所以我们要重写equals()方法,按照原则,我们重写了equals()方法,也要重写hashCode()方法。

Java中的超类Object类中定义的equals()方法是用来比较两个引用所指向的对象的内存地址是否一致,Object类中equals()方法的源码:

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

Object类中hashCode()方法的源码:

public native int hashCode();

Object类中的hashCode()方法,用的是native关键字修饰,说明这个方法是个原生函数,也就说这个方法的实现不是用java语言实现的,是使用c/c++实现的,并且被编译成了DLL,由java去调用,jdk源码中不包含。对于不同的平台它们是不同的,java在不同的操作系统中调用不同的native方法实现对操作系统的访问,因为java语言不能直接访问操作系统底层,因为它没有指针。

(1)这种方法调用的过程:

1、在java中申明native方法,然后编译;
2、用javah产生一个 .h 文件;
3、写一个 .cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(其中又包含了jdk带的jni.h文件);
4、将.cpp文件编译成动态链接库文件;
5、在java中用System.loadLibrary()文件加载第四步产生的动态链接库文件,然后这个navite方法就可被访问了
Java的API文档对hashCode()方法做了详细的说明,这也是我们重写hashCode()方法时的原则【Object类】;

(2)重点要注意的是:

a. 在java应用程序运行时,无论何时多次调用同一个对象时的hashCode()方法,这个对象的hashCode()方法的返回值必须是相同的一个int值;

b. 如果两个对象equals()返回值为true,则他们的hashCode()有返回相同的int值;

c. 如果两个对象equals()返回值为false,则他们的hashCode()值也有可能相同;

现在,我们到这里可以看出,我们重写了equals()方法也要重写hashCode()方法,这是因为要保证上面所述的b,c原则;所以java中的很多类都重写了这两个方法,例如String类,包装类等。

三、在什么情况下需要重写hashCode()方法和equals()方法

当我们自定义的一个类,想要把它的实例保存在集合中时,我们就需要重写这两个方法;集合(Collection)有两个类,一个是List,一个是Set。

List:集合中的元素是有序的,可以重复的;
Set:无序,不可重复的;12

(1)以HashSet来举例:

HashSet存放元素时,根据元素的hashCode方法计算出该对象的哈希码,快速找到要存储的位置,然后进行比较,

比较过程如下:

  • 如果该对象哈希码与集合已存在对象的哈希码不一致,则该对象没有与其他对象重复,添加到集合中!
  • 如果存在于该对象相同的哈希码,那么通过equals方法判断两个哈希码相同的对象是否为同一对象(判断的标准是:属性是否相同)
    • 相同对象,不添加。
    • 不同对象,添加。

注意:如果返回值为false,则这个时候会以链表的形式在同一个位置上存放两个元素,这会使得HashSet的性能降低,因为不能快速定位了。示意图如下:

这里写图片描述

还有一种情况就是两个对象的hashCode()返回值不同,但是equals()返回true,这个时候HashSet会把这两个对象都存进去,这就和Set集合不重复的规则相悖了;所以,我们重写了equals()方法时,要按照b,c规则重写hashCode()方法!

四、如何重写这两个方法

如果你决定要重写equals()方法,那么你一定还要明确这么做所带来的风险,并确保自己能写出一个健壮的equals()方法。

一定要注意的一点是,在重写equals()后,一定要重写hashCode()方法。

(1)我们先看看 JavaSE 8 Specification中对equals()方法的说明:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).

这段话用了很多离散数学中的术数.简单说明一下:

  1. 自反性:A.equals(A)要返回true;
  2. 对称性:如果A.equals(B)返回true, 则B.equals(A)也要返回true;
  3. 传递性:如果A.equals(B)为true, B.equals©为true, 则A.equals©也要为true. 说白了就是 A = B , B = C , 那么A = C;
  4. 一致性:只要A,B对象的状态没有改变,A.equals(B)必须始终返回true;
  5. A.equals(null) 要返回false;

(2)简单步骤:

为了说明方便,我们先定义一个程序员类(Coder):

class Coder {  private String name;  private int age;  // getters and setters  
} 

我们想要的是,如果2个程序员对象的name和age都是相同的,那么我们就认为这两个程序员是一个人。这时候我们就要重写其equals()方法。因为默认的equals()实际是判断两个引用是否指向内在中的同一个对象,相当于 == 。 重写时要遵循以下三步:

1、判断是否等于自身:

if(other == this){return true;  
}

2、 使用instanceof运算符判断 other 是否为Coder类型的对象:

if(!(other instanceof Coder))  {return false;  
}

3、比较Coder类中你自定义的数据域,name和age,一个都不能少:

Coder o = (Coder)other;  
return o.name.equals(name) && o.age == age;

看到这有人可能会问,第3步中有一个强制转换,如果有人将一个Integer类的对象传到了这个equals中,那么会不会扔ClassCastException呢?这个担心其实是多余的.因为我们在第二步中已经进行了instanceof 的判断,如果other是非Coder对象,甚至other是个null, 那么在这一步中都会直接返回false, 从而后面的代码得不到执行的机会。

上面的三步也是<Effective Java>中推荐的步骤,基本可保证万无一失。

我们在大学计算机数据结构课程中都已经学过哈希表(hash table)了,hashCode()方法就是为哈希表服务的。

当我们在使用形如HashMap, HashSet这样前面以Hash开头的集合类时,hashCode()就会被隐式调用以来创建哈希映射关系。

<Effective Java>中给出了一个能最大程度上避免哈希冲突的写法,但我个人认为对于一般的应用来说没有必要搞的这么麻烦.如果你的应用中HashSet中需要存放上万上百万个对象时,那你应该严格遵循书中给定的方法.如果是写一个中小型的应用,那么下面的原则就已经足够使用了:

要保证Coder对象中所有的成员都能在hashCode中得到体现.

@Override  
public int hashCode() {  int result = 17;  result = result * 31 + name.hashCode();  result = result * 31 + age;  return result;  
}

其中int result = 17你也可以改成20, 50等等都可以.看到这里我突然有些好奇,想看一下String类中的hashCode()方法是如何实现的.查文档知:

“Returns a hash code for this string. The hash code for a String object is computed as

s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.)”

对每个字符的ASCII码计算n - 1次方然后再进行加和,可见Sun对hashCode的实现是很严谨的. 这样能最大程度避免2个不同的String会出现相同的hashCode的情况.


参考文章:

1、http://blog.csdn.net/jing_bufferfly/article/details/50868266
2、http://blog.csdn.net/neosmith/article/details/17068365

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

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

相关文章

bloomberg用法 固定收益_内部收益率的用法浅谈某保险的真实收益率

公司财务课程,魏教授讲得非常棒,治愈了大家的瞌睡,激起了大家的学习热情。我一直认为,大牛在讲课,吹牛还是演讲中,都能够给现场观众提供强大的能量,使得彼此运行在同一个共振频率。内部收益率(I…

联想小新锐7000主板型号_2020年高性价比游戏本选购推荐指南——7000价位游戏本推荐...

玩电脑的老猫,只说你最想听的话!大家好,我是老猫,这段时间很多小伙伴问我,多少钱能买到一台不错的游戏本呢?知乎中很多人说5000元买游戏本,就是等于买一台“电子垃圾”虽然5000元的游戏本不是非…

Java重写equals方法时为什么要重写hashCode方法

在我们平时编写Java代码时,重写equals方法时一定要重写hashCode方法,这是为什么呢? 在讨论这个问题前,我们先看下Object类中hashCode方法和equals方法。 hashCode方法: 翻译如下: equals方法:…

iot nb 曹淑敏 鹰潭_移动物联时代来啦 鹰潭一项成就为全球首个 江西力争在全国“四个领先”...

移动物联网是基于移动通信网络的物联网技术体系,主要由NB-IoT(窄带物联网技术)、eMTC(增强机器类通信技术)、蜂窝网的未来演进技术5G(第五代移动通信技术)三种技术共同构成。江西网络广播电视台9月1日讯(记者 李希文)9月1日下午,江西省工信委、中国信息通…

RPC(一)[概述]

文章目录RPC-概述简介1.服务的调用过程2.RPC框架1.Dubbo–电商2.Motan–互联网3.Thrift4.gRPC5.RPCX3.RPC 和 RESTful1.RPC over HTTP 和 RESTful2.RPC over TCP 和 RESTfulRPC-概述 简介 远程过程调用(Remote Procedure Call,缩写为 RPC)是…

epic登陆按钮一直在转_双剑合璧!GOG GALAXY 2.0官方支持Epic商店集成

2020年7月21日晚,GOG官方宣布提供与Epic商城的集成服务,这也是GOG GALAXY 2.0推出以来又一次与超大型游戏平台达成的合作。现在玩家可以通过GOG GALAXY 2.0管理、安装和启动来自Epic商城的游戏。需要注意的是,因为这次行为是完全官方的&#…

用 Java 实现人脸识别功能

引言 人脸识别SDK 人脸识别技术是很复杂的,自己用Java手撕一个识别算法有点不切实际,毕竟实力不允许我这么嚣张,还是借助三方的SDK吧! 找了一圈发现一个免费的人脸识别SDK: ArcSoft:,地址:ht…

有没有词匹配算法_Google Ads 再次扩展了关键字变量匹配

在过去的几年里,Google Ads已经扩大了使用相近的关键字变量。Google改变的匹配类型不仅包括拼写错误和复数,还包括功能词、修改和重新排序。日前,谷歌再次扩展了相近变量的使用。短语匹配和广泛匹配修饰语以下是2019年7月31日谷歌官方广告公告…

mysql 组合查询_MySQL组合查询教程

#MYSQL#这是我MySQL的第八篇教程。本篇主要介绍的是MySQL的组合查询的用法。在大多数的SQL查询中都只包含从一个或者多个表中返回数据的单条SELECT语句,MySQL其实也支持允许执行多条SELECT语句,并将结果作为单个结果集返回,这些组合查询通常称…

Java后端 + 百度SDK实现人脸识别

Java后端 百度SDK实现人脸识别 人工智能越来越贴近我们的生活,相信大家也经常接触到人脸识别,手机付款、app注册验证、门禁等等。 如果要用Java后台使用这些功能,那么需要怎么做呢?请看完下面这篇文章,就能轻松、简单…

路由器局域网设置_路由器基础介绍

路由器已经商用很多年了,但是一些SSID, DHCP, 这些名词都还没有搞懂,遇到路由器问题,不知所措,只能求助于网管。但是有些问题是很简单的,只要稍微去学习就可以解决问题。下面介绍路由器的一些简单问题。路由…

mysql主键设置after_mysql如何改变主键属性

mysql改变主键属性的方法:1、使用关键字modify,代码为【alter table tbl_name modify.....】;2、使用关键字change,代码为【alter table tbl_name change..】。本教程操作环境:windows7系统、mysql8.0.22版&#xff0c…

python打包成exe_Python打包成exe时,再犯这几个错误就说不过去了

大家好,我是今天的值班小编,不知道朋友们开工了没有,小编的公司已经远程办公好几天了。今天由于写游戏教程的那位小编弃坑不更新文章了,只好由本小编继续来解决粉丝朋友们的问题。 如果朋友们在使用python的过程中遇到了任何问题&…

机器视觉 光学工程专业_瑞士Idonus MEMS制造设备 创新技术 机器视觉测量(远心光学)...

机器视觉从图像处理算法到光学安装,idonus 提供部分或全部机器视觉解决方案。典型应用包括机械零件的高通量自动测量、表面质量检测、定位、质量控制或识别。我们定制开发的解决方案可与机器人化或手动设备相结合。我们的报价:定制硬件和软件&#xff0c…

vs 中使用32 位mysql_vs2010连接mysql数据库(含win32和x64两种平台)

数据库安装: 此处有两种安装方式,第一种使用xxx.msi图形化安装方式,和普通的exe软件安装方式一样,按照默认选项一直下一步就可以。mysql下载地址为http://dev.mysql.com/downloads/windows/installer/ 第二种是在mysql官网下载zip…

电脑时间校对器_笔记本电脑如何保养?华为教你五招轻松延长使用时间

集微网7月11日消息(文/数码控),近日华为花粉俱乐部官方微信公众号发表长文教用户如何保养笔记本电脑,具体有以下五招:

uvc能支持多大分辨率_华为手环B5测评:该有的功能一个不缺,甚至还能变身蓝牙耳机...

前阵子,华为召开nova5系列新品发布会的同时,也给用户们带来了华为手环B5的新配色--铅石青。华为手环B5在去年7月发布,分为三个版本:运动版999元、商务版1199元、时尚版1499元。​此次更新的是运动版,售价仍为999元&…

cf不能全屏win7的解决方法_win7系统局域网不能访问怎么办 win7局域网不能访问解决方法...

在局域网当中比较实用的功能要数共享文件了,局域网共享可以让用户们共享和管理资源更加便捷,可以大大提高工作效率,不过最近有位win7系统用户使用电脑的时候,发现电脑无法访问局域网中的其他任何一台电脑,这让用户不知…

CGLib动态代理原理

CGLib动态代理原理 CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法; 看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方…

sql读取excel数据_Python 读取 Excel 数据,并插入到MySQL

说实话,个人不建议用Python来读取Excel进行入库操作,有条件的话,可以尝试用 ETL 工具,快速导入到MySQL中,或者也可使用 SQL 的导入工具进行。写本文的目的在于:记录一下之前做过一次这个同类型的东西。也为…