文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

重写Equals方法为什么通常会重写Hashcode方法?

2024-12-03 09:12

关注

本文转载自微信公众号「程序新视界」,作者二师兄。转载本文请联系程序新视界公众号。  

最近在面试的时候,当问完了HashMap的数据结构之后,通常会再多问一个问题,就是:重写equals方法时通常为什么也要重写一下hashcode方法?

其实这个问题,本质上又回到HashMap的应用场景了,就是想看一下面试者是否真的融会贯通。今天这篇文章就带大家了解一下equals方法和hashcode方法之间的关系,以及相关的知识点。

equals与hashcode的存在

其实每个类都有一个equals方法和hashcode方法。因为所有的类都继承自Object类。Object类中定义如下:

  1. public boolean equals(Object obj) { 
  2.     return (this == obj); 
  3.      
  4. public native int hashCode(); 

直观上可以看到equals方法默认比较的是对象的引用,直接用“==”进行比较。而hashCode方法是一个native方法,返回值为整型。

而这两个方法都未被final修饰,都是可以进行重写的。

对于我们经常使用的比如String 、Math、Integer、Double等类,都进行了equals()和hashCode()方法的重写。

equals()方法

equals()方法是用来判断两个对象是否相等。Object默认实现了equals方法,但很明显不太符合个性化的需求,因此往往需要进行重写。比如常用的String类,重写的equals方法如下:

  1. // 重写equals方法 
  2. public boolean equals(Object anObject) { 
  3.     if (this == anObject) { 
  4.         return true
  5.     } 
  6.     if (anObject instanceof String) { 
  7.         String anotherString = (String)anObject; 
  8.         int n = value.length; 
  9.         if (n == anotherString.value.length) { 
  10.             char v1[] = value; 
  11.             char v2[] = anotherString.value; 
  12.             int i = 0; 
  13.             while (n-- != 0) { 
  14.                 if (v1[i] != v2[i]) 
  15.                     return false
  16.                 i++; 
  17.             } 
  18.             return true
  19.         } 
  20.     } 
  21.     return false

这里的比较已不再是单纯的地址比较了。首先通过地址进行比较,如果地址相同那么肯定是相同的对象。如果地址不同就再拿char数组的内容进行比较,完全相等则返回true。

equals()方法的特质

在Object类的equals方法上有注释说明了equals()方法需满足的一些特性:

对照上面特质,我们发现Object方法直接比较的是两个引用地址,只有两个地址相同才相等,也就是说是差别可能性最大的等价关系。

而String的equals方法,不仅包含应用地址相同这种情况,还包括里面所存储的字符串值相同的情况。也就是说虽然是两个String对象,但是它们的字符串值相等,那么equals方法返回的结果就是true。这也正是大多数情况下我们所说的“equals方法比较的是值”。

由于Object的equals方法的默认特例存在,因此在没有自定义equals方法时,我们不能一概的说equals方法比较的是具体的值,而“==”比较的是引用。

hashCode()方法

hashCode()方法返回对象的一个hash code值。该方法被用于hash tables,如HashSet、HashMap。

hashCode()是一个native方法,返回值类型是整形,并且可以被重写。

Object中的native hashCode()方法将对象在内存中的地址作为哈希码返回,可以保证不同对象的返回值不同。

还以String类为例,它的hashCode方法为:

  1. // 重写hashCode方法 
  2. public int hashCode() { 
  3.     int h = hash; 
  4.     if (h == 0 && value.length > 0) { 
  5.         char val[] = value; 
  6.  
  7.         for (int i = 0; i < value.length; i++) { 
  8.             h = 31 * h + val[i]; 
  9.         } 
  10.         hash = h; 
  11.     } 
  12.     return h; 

上述hash值的计算注释中有说明,基本公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]。

其中, s[i]是字符串的第i个字符,n是字符串的长度,^表示求幂(空字符串的哈希码为0)。

计算过程中使用数字31,主要有以下原因:

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

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

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

hashCode()方法的作用

前面提到hashCode()方法主要用于hash表中,比如HashSet、HashMap等。

我们先来看一下ArrayList,它的底层是数组,每个数据往底层的数组中存取即可,数据不需要判断是否重复。

集合Set中的元素是无序不可重复的,那么如何确保存入的元素不重复呢?逐个调用equals()方法进行比较?数据量少的时候还可以,但数据量大了时间复杂度基本上是O(n),会出现性能问题。

Java中采用哈希算法来解决这个问题,将对象(或数据)依特定算法直接映射到一个地址上,这样时间复杂度趋于O(1),对象的存取效率大大提高。

集合Set添加某元素时,先调用hashCode()方法,定位到此元素实际存储位置,如果这个位置没有元素,说明是第一次存储;若此位置有对象存在,调用equals()进行比较,相等就舍弃此元素不存,不等则散列到其他地址。

上面的示例也说明了为什么equals()相等,则hashCode()必须相等,进而当重写了equals方法,也要对hashCode()方法进行重写。

HashMap的基本处理机制与HashSet很类似,只不过底层的数据存储结构有所不同而已。

简而言之,在集合查找时,hashcode能极大的降低对象比较次数,提高查找效率。

hashCode()方法的性质

hashCode的实现也有一定的要求,相关英文说明在Object的equals方法注解上:

如何重写hashCode()

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

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

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

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

小结

关于equals方法很明确的是用于比较两个对象是否相等。而对于hashCode方法重点是为了在类似HashMap场景下提升效率,只算是技术要求。

在集合中通常通过equals方法来比较对象是否相等,通过hashCode方法来解决大数据量时会发生的性能问题。

在实践中我们很少使用Object对象来作为Map的key,也是因为如果Object对象的属性变了,会导致hashCode变化,进而可能会导致找不到对应值,而String是不可变的对象,作为key就很适合。

参考文章:

https://www.cnblogs.com/kismetv/p/7191736.html

https://www.iteye.com/blog/kakajw-935226

https://www.iteye.com/blog/bijian1013-1972404

来源:程序新视界内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯