java为什么要重写hashCode和equals方法?
如果不被重写(原生)的hashCode和equals是什么样的?
- 不被重写(原生)的hashCode值是根据内存地址换算出来的一个值。
- 不被重写(原生)的equals方法是严格判断一个对象是否相等的方法(object1 == object2)。
为什么需要重写equals和hashCode方法?
在我们的业务系统中判断对象时有时候需要的不是一种严格意义上的相等,而是一种业务上的对象相等。在这种情况下,原生的equals方法就不能满足我们的需求了
所以这个时候我们需要重写equals方法,来满足我们的业务系统上的需求。那么为什么在重写equals方法的时候需要重写hashCode方法呢?
我们先来看一下Object.hashCode的通用约定(摘自《Effective Java》第45页)
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回
同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。 - 如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。
- 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表(hash
table)的性能。
如果只重写了equals方法而没有重写hashCode方法的话,则会违反约定的第二条:相等的对象必须具有相等的散列码(hashCode)
同时对于HashSet和HashMap这些基于散列值(hash)实现的类。HashMap的底层处理机制是以数组的方法保存放入的数据的(Node<K,V>[] table),其中的关键是数组下标的处理。数组的下标是根据传入的元素hashCode方法的返回值再和特定的值异或决定的。
如果该数组位置上已经有放入的值了,且传入的键值相等则不处理,若不相等则覆盖原来的值,如果数组位置没有条目,则插入,并加入到相应的链表中。检查键是否存在也是根据hashCode值来确定的。所以如果不重写hashCode的话,可能导致HashSet、HashMap不能正常的运作、
如果我们将某个自定义对象存到HashMap或者HashSet及其类似实现类中的时候,如果该对象的属性参与了hashCode的计算,那么就不能修改该对象参数hashCode计算的属性了。有可能会移除不了元素,导致内存泄漏。
扩展:
在重写equals方法的时候,需要遵守下面的通用约定:
1、自反性。
对于任意的引用值x,x.equals(x)一定为true。
2、对称性。
对于任意的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)也一定返回true。
3、传递性。
对于任意的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
4、一致性。
对于任意的引用值x和y,如果用于equals比较的对象没有被修改的话,那么,对此调用x.equals(y)要么一致地返回true,要么一致的返回false。
5、对于任意的非空引用值x,x.equals(null)一定返回false。
重写hashCode方法的大致方式:
a、把某个非零常数值,比如说17(最好是素数),保存在一个叫result的int类型的变量中。
b、对于对象中每一个关键域f(值equals方法中考虑的每一个域),完成一些步骤:
1、为该域计算int类型的散列吗c:
1)、如果该域是boolean类型,则计算(f?0:1)。
2)、如果该域是byte、char、short或者int类型,则计算(int)f。
3)、如果该域是float类型,则计算Float.floatToIntBits(f)。
4)、如果该域是long类型,则计算(int)(f ^ (f>>>32))。
5)、如果该域是double类型,则计算Double.doubleToLongBits(f)得到一个long类型的值,然后按照步骤4,对该long型值计算散列值。
6)、如果该域是一个对象引用,并且该类的equals方法通过递归调用equals的方式来比较这个域,则同样对这个域递归调用hashCode。如果要求一个更为复杂的比较,则为这个域计算一个“规范表示”,然后针对这个范式表示调用hashCode。如果这个域的值为null,则返回0(或者其他某个常数)
7)、如果该域是一个数组,则把每一个元素当做单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤下面的做法把这些散列值组合起来。
2、按照下面的公式,把步骤1中计算得到的散列码C组合到result中:
result = 31*result+c。
c、返回result。
d、写完hashCode方法之后,问自己“是否相等的实例具有相等的散列码”。如果不是的话,找出原因,并修改。
可以通过org.apache.commons.lang.builder.HashCodeBuilder这个工具类来方便的重写hashCode方法。
java为什么要重写hashCode和equals方法?的更多相关文章
- 【java编程】重写HashCode和equals方法
[一]重写equals方案的规则 equals方法本来的原则 1.类的每个实例本质上都是唯一的. 2.不关心类是否提供了“逻辑相等”的测试功能 3.超类已经覆盖了equals,从超类继承过来的行为对于 ...
- 重写Java Object对象的hashCode和equals方法实现集合元素按内容判重
Java API提供的集合框架中Set接口下的集合对象默认是不能存储重复对象的,这里的重复判定是按照对象实例句柄的地址来判定的,地址相同则判定为重复,地址不同不管内容如何都判定为不重复,这有时与需求不 ...
- Java 中正确使用 hashCode 和 equals 方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...
- 为什么要重写hashcode和equals方法?初级程序员在面试中很少能说清楚。
我在面试 Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分, ...
- (转)Java 中正确使用 hashCode 和 equals 方法
背景:最近在编写持久化对象时候遇到重写equals和hashCode方法的情况,对这两个方法的重写做一个总结. 链接:https://www.oschina.net/question/82993_75 ...
- (转)为什么要重写 hashcode 和 equals 方法?
作者丨hsm_computer cnblogs.com/JavaArchitect/p/10474448.html 我在面试Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候 ...
- Java中正确使用hashCode和equals方法
在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...
- HashMap中使用自定义类作为Key时,为何要重写HashCode和Equals方法
之前一直不是很理解为什么要重写HashCode和Equals方法,才只能作为键值存储在HashMap中.通过下文,可以一探究竟. 首先,如果我们直接用以下的Person类作为键,存入HashMap中, ...
- 为什么要重写hashcode和equals方法
我在面试 Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分, ...
随机推荐
- 爱奇艺直播 - 春晚直播业务API架构
小结: 1.服务熔断策略 在网关服务中经常会对后端不同api接口做服务聚合,比如A服务 -> B服务 -> C服务 ,如果C服务出现问题,那么在调用C服务之前需要做熔断.而在设计熔断器的时 ...
- HttpServlet
HttpServlet的原理 HttpServlet抽象类中的(部分)方法 HttpServlet extends GenericServlet{ void service(ServletReques ...
- ZD6转辙机
1.转辙机的分类? 2.ZD6转辙机的电源电压是多少? 3.ZD6转辙机的电路图是怎么样的? 经常看到的一些术语: DBJ,FBJ DCJ FCJ DBJ 定位表示继电器 FBJ 反位表示继电器 DC ...
- JMeter 压测基础(四)——Java工程测试
JMeter Java工程测试 1.eclipse中新建工程 2.导入需要的jar包 从JMeter安装目录lib/ext中两个jar包buildPath到Java project中,分别是“Apac ...
- Python004-数据处理示例:以某个数据(字段)为基准从数据中获取不同的字段行数
数据源样式如下所示: 需求: 读取文本,以第一列为基准参考系,每个基准仅输出满足需要条数的数据:不满足,全部输出. 比如,基准为 6236683970000018780,输出条数要求为 5.若文本中含 ...
- linux /proc/sys/vm/中各个文件含义
1) /proc/sys/vm/block_dump该文件表示是否打开Block Debug模式,用于记录所有的读写及Dirty Block写回动作. 缺省设置:0,禁用Block Debu ...
- Spark入门到精通--(第九节)环境搭建(Hive搭建)
上一节搭建完了Hadoop集群,这一节我们来搭建Hive集群,主要是后面的Spark SQL要用到Hive的环境. Hive下载安装 下载Hive 0.13的软件包,可以在百度网盘进行下载.链接: h ...
- python中闭包的理解
闭包的三个条件: 1.函数(外函数)中定义了内函数:2.内函数使用了外函数的非全局变量:3.外函数最终返回的是内函数的引用. 简单闭包事例: #outerfunc为外函数 def outerfunc( ...
- MySQL InnoDB Online DDL学习
MySQL Online DDL这个新特性是在MySQL5.6.7开始支持的,更早期版本的MySQL进行DDL对于DBA来说是非常痛苦的.现在主流版本都集中在5.6与5.7,为了更好的理解Online ...
- ionic3 小记录
cordova platform add ios@latest 安装最新ios ionic cordova build ios -- --buildFlag="-UseModernBuild ...