文章目录
1.问题
相信大家和我一样经常在面试题中看到这一个问题:为什么重写 equals 时必须重写 hashCode 方法。那为什么这个问题问得这么频繁,我个人觉得要理清这个问题,背后隐藏的是对哈希表数据结构的理解。
2.hashCode产生(个人理解,也可能不对)
要回答上面的问题,首先一个就是要深入理解hashCode的产生,关于hashCode的产生,很多初学者很可能跟我一样认为它的产生是通过对象的地址经过计算而来,我认为这其实是一个不完全正确。,实际上它的产生与地址可以相关,也可以无关(与对象的内部状态有关)。
Object类的hashCode()方法默认实现会使用对象的内存地址作为哈希码的一部分,此时它的产生与对象内存地址相关。
一般情况下我们会重写hashCode方法,使用对象的内部状态产生hashCode,此时与对象的内存地址就无关了。
那为什么同一个对象产生的hashCode相同,而不同对象产生的hashCode也可能相同呢?
我们以Object中的hashCode为例
对于这个问题,我们回到初中时的函数的定义:
从输入出发,一个函数不同的输入可能得出相同的结果。
从结果出发,一个函数得出不同的结果,那么函数的输入一定不同。
同理,我们对象的内存地址为输入,而得出的hashcode为输出。即对象的内存地址不同可能得出的hashCode可能相同,但是hashCode不相同,对象内存地址一定不同。(对象内存地址相同是同一个对象,对象内存地址不相同是不同对象)
同理,我们以对象的内部状态为例子:
体重,身高分别不同,经过f得出的结果未必不同。
但是得出的结果不同,体重,身高至少有一项肯定不同。
3.equals是() 与 ==
- equals默认没有覆盖的情况下我们比较的是两个对象的内存地址(
(1)换句话,也就是直接比较两个对象是否为同一个对象。
(2)等同与 双等号 (基本数据类型 双等号比较的是值, 引用数据类型双等号比较的是内存地址)。 - 通常我们会覆盖equals,从而去比较两个对象的内容是否相等。
4.equals和hashCode配合工作流程
!!!注意这里对equals和hashCode进行了重写,当然这里的hashCode重写不规范。只是为了说明问题
我们重写equals后。我们后续使用equals比较对象,就是要比较的是内容。
如下面的例子。我们在HashMap中存储了一些人的信息,现在来了一个名叫徐老九的人,要在HashMap集合中找到他的信息,如果没有,则将他的信息写入HashMap集合中,如果我们单纯使用equals,则需要从头比较到尾,效率非常低。
如果我们使用重写后的hashCode(160 + 56 = 216),可以直接定位徐老九所在的下标,但我们还要使用equals去比较徐老九是否真的存在。
5.为什么重写 equals 时必须重写 hashCode 方法?
现在我们回到正题。
如果我们重写equals时没有重写hashCode,那么可能会出现有两个内容相等的对象,但是它们的hashCode不同。这会导致集合无法正确处理这些对象。
比如下面的例子,hashMap集合中已经有一个徐老九,但是一个新的徐老九(名字 身高 体重一致,应该认为是同一个对象)与原来的徐老九因为地址不同得出的hashCode不同,从而错误认为信息当中没有徐老九,导致错误发生。
6.一些问题
作者:Listener-1379
出处:Listener-1379博客
github地址:https://github.com/beyong2019
本博客中未标明转载的文章归作者Beyong有,欢迎转载,但未经作者同意必须保留此段声明,且在文章明显位置给出原文连接,否则保留追究法律责任的权利。