hashCode() 和 equals() 的区别、为什么重写hashCode() 就要重写 qeuals()

本文针对问题:hashCode() 和 equals() 的区别,为什么重写hashCode() 就要重写 qeuals() ,和为什么重写hashCode() 就要重写 qeuals() 进行讲解(超详细)

一、hashCode 的特性


  1. hashcode 特性体现主要在它的查找快捷性,在 Set 和 Map 这种使用哈希表结构存储数据的集合中。HashCode 方法的就大大体现了它的价值,主要用于在这些集合中确定对象在整个哈希表中存储的区域。
  2. 如果两个对象相同,则着两个对象的equals方法返回的值一定为 true,两个对象的 HashCode 方法返回的值也一定相同。
  3. 如果两个对象返回的 HashCode 的值相同,但不能够说明这两个对象的 equals 方法返回的值就一定为 true,只能说明这两个对象在存储在哈希表中的一个桶中。
  4. 如果一个对象 equals 方法被重写,那么该对象的 HashCode 方法也应该被重写(具体原因下面再介绍)

二、hashCode 的作用


在java中集合主要有三种,分别是 List、Set、Map,其中 Set 和 Map 中都是用了哈希表结构,在 Set 中它是不能存储相同元素的,要说 HashCode 方法的作用先得说 Set 和 Map 中存储一个元素的过程,由于 Set 是不能够存储相同元素,则每次给 set 中添加元素的时候第一步先判断集合中是否存在该元素,如果存在则舍弃不进行存储,如果不存在则进行存储。在 java 中判断两个对象是否相等一般都使用equals 方法进行判断。比如现在 Set 中已经存在1000个结构复杂的对象,再添加第1001个对象时,它会在集合中进行对比,看是否已经存在,如果存在则舍弃,结束添加操作。那如果不存在,则它会将要添加的对象与集合中1000个对象进行 equals 操作判断相等,最后再进行添加,那么这样程序的效率就会很低。于是 java 采用了哈希表的原理。哈希算法也叫散列算法,是将数据依据特定算法直接指定到一个地址上来。

  1. 如果该物理地址上没有元素存在,则直接将该数据存储在该地址上,不用再进行任何比较了。
  2. 如果该地址上存在元素,则进行比较如果相同则舍弃,不再进行存储与比较了。
  3. 如果不相等的话则发生了 hash 冲突的情况,在 set和map 中的处理方式是在该位置上生成一根链表将产生的 hash 冲突并且 equals 不相同的对象(拉链发),挂在这根链表上,则每次添加元素时就不用和整个集合中的元素进行比较,而只需要使用 HashCode 定位到该数据应该存储的地址上,然后与该地址上的对象进行比较即可。

采用哈希表以及 HashCode 的这种方法,会大大提高类似于 Set 和 Map 这种存储方式的效率。所以 HashCode 在上面的过程中主要扮演了寻找每个对象在集合中具体存储区域的功能,每个对象都可以计算出他们的 hash 码,按 hash 码进行分组,每个分组对应着一个存储区域,根据一个对象的 hash 码就可以确定该对象的存储区域,这样就大大减少了查询匹配元素的数量,大大提高了效率。

三、为什么重写equals一定也要重写HashCode方法?


在 java 中 equals 方法用于判断两个对象是否相等,而 HashCode 方法在java中主要由于哈希算法中的寻域的功能(也就是寻找数据应该存储的区域的)。在类似于set和map 集合的结构中,java 为了提高在集合中查询匹配元素的效率问题,引入了哈希算法,通过某种算法及我们的 HashCode 方法得到对象的hash 码,再通过hash码推算出数据应该存储的位置。然后再进行 equals 操作进行匹配,减少了比较次数,提高了效率。在集合做了优化之后进行判断元素相等的过程是这样的,首先判断两个对象的HashCode 方法返回的值是否相等,如果相等然后再判断两个对象的 equals 方法,如果 HashCode 方法返回的值不相等,则直接会认为两个对象不相等,不进行equals方法的判断。有这样一个场景有两个Student 对象,equals 方法认为如果两个对象的学号相同则认为这两个对象相同。可是如果没有重写HashCode 方法只重写了 equals 方法,此刻并不能实现我们的要求,**它首先会判断 HashCode 方法返回的值是否相等,由于我们没有重写 HashCode 方法,此时返回的值是不同的,因此不会去判断我们重写的 equals 方法。**而如果重写 HashCode 方法不重写 equals 方法也是同样的效果,不重写 equals 方法实际是调用 Object 方法中的 equals 方法,判断的是两个对象的堆内地址。而我们重写的 HashCode 方法认为相等的两个对象在 equals 方法处并不相等。因此重写 equals 方法时一定也要重写HashCode 方法,重写 HashCOde 方法时也应该重写 equals 方法。

四、equals()不等而HashCode()返回的值却有可能相同?


HashCode方法实际上是通过一种算法得到一个对象的hash码,这个hash码是用来确定该对象在哈希表中具体的存储区域的。返回的hash码是int类型的所以它的数值范围为 [-2147483648, +2147483647] 之间的,而超过这个范围,实际会产生溢出,溢出之后的值实际在计算机中存的也是这个范围的。

比如最大值 2147483647 + 1 之后并不是在计算机中不存储了,它实际在计算机中存储的是 -2147483648。在java中对象可以有很多很多通过 new 关键字来产生。而hash码也是通过特定算法得到的,所以很难或者说几乎没有什么算法在这个范围内在这个情况下不会不产生相同的 hash 码的。

也就是说在上述情况下肯定是会发生哈希碰撞的,因此不同对象可能有相同的 HashCode 的返回值。也有人说 Object 方法中的 HashCode 方法是通过内存地址得来的,是唯一的。可是 HashCode 方法是共有的,也就意味着它是可以被程序员重写的。因此不同环境下实现 HashCode 的算法可能不同。因此 equals 方法返回结果不相等,而 HashCode 方法返回的值却有可能相同!

五、总结


得分点 :

  • hashCode() 用途、equals() 用途
  • hashCode()、equals() 约定标准

hashCode() 方法的主要用途是获取哈希码,equals() 主要用来比较两个对象是否相等。二者之间有两个约定,如果两个对象相等,它们必须有相同的哈希码;但如果两个对象的哈希码相同,他们却不一定相等。也就是说,equals() 比较两个对象相等时 hashCode() 一定相等,hashCode() 相等的两个对象 equqls()不一定相等,这就是哈希冲突,为了解决 hash 冲突,必须一定重写。

加分回答:
Object类提供的 equals() 方法默认是用 == 来进行比较的,也就是说只有两个对象是同一个对象时,才能返回相等的结果。而实际的业务中,我们通常的需求是,若两个不同的对象它们的内容是相同的,就认为它们相等。鉴于这种情况,Object类中equals()方法的默认实现是没有实用价值的,所以通常都要重写。 由于 hashCode() 与 equals() 具有联动关系,所以 equals() 方法重写时,通常也要将 hashCode()进行重写,使得这两个方法始终满足相关的约定。



参考文章:

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重写hashCode()和equals()方法是为了确保对象在比较和存储时能够正确地工作。当我们使用HashMap等集合类时,它们底层使用散列表来存储对象,而散列表使用hashCode()方法来计算对象的哈希值,然后使用equals()方法来比较对象是否相等。 hashCode()方法的作用是返回对象的哈希码,它用于确定对象在散列表中的位置。在使用HashMap等散列表实现的集合类时,如果我们没有重写equals()方法,而只重写hashCode()方法,那么当两个对象通过equals()方法比较相等时,它们的哈希码可能会不相等,导致这两个对象在散列表中被认为是不同的对象,从而无法正确地获取到存储在散列表中的值。 因此,为了确保对象的比较和存储的一致性,我们需要同时重写hashCode()和equals()方法。通过重写equals()方法,我们可以定义对象相等的条件,使得散列表在比较对象是否相等时按照我们自己的逻辑来进行判断。而通过重写hashCode()方法,我们可以确保相等的对象返回相同的哈希码,以保证它们能够被正确地存储和查找。 总结来说,重写hashCode()和equals()方法是为了确保对象在比较和存储时的一致性和正确性。重写equals()方法来定义对象相等的条件,而重写hashCode()方法来确保相等的对象返回相同的哈希码。这样可以保证集合类在使用时能够正确地工作,并且可以在需要时准确地找到对象。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [为什么使用HashMap需要重写hashcodeequals方法_HashMap常见问题](https://blog.csdn.net/weixin_39720510/article/details/109917422)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [重写hashCode()和equals()方法详细介绍](https://download.csdn.net/download/weixin_38674763/12765397)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值