关于equals和hashCode方法

本篇文章记录学习$equals()$方法和$hashCode()$方法的内容。

什么是$hashCode$

通常所说的$hashCode$是一个经过哈希运算后的整型值,这个哈希运算的方法,定义在$Object$类中,通过一个本地方法$hashCode()$来实现(在$hashMap$中还会有一些其他的运算)。

1
public native int hashCode();

要了解这个方法到底是什么作用,最有效的方法就是直接看源码注释。

image-20211203203212634

返回当前对象的一个哈希值。这个方法用于支持一些哈希表,例如$hashMap$。

通常来讲,它有一下一些约定:

  • 若对象的信息没有被修改,那么,在一个程序的执行期间,对于相同的对象,不管调用多少次$hashCode$方法,都应该返回相同的值。当然,在相同程序的不同执行期间,不需要保持结果的一致。
  • 若两个对象的$equals()$方法返回值相同,那么,他们调用各自的$hashCode$方法时,也必须返回相同的结果
  • 当两个对象的$equals()$方法返回值不同时,那么他们的$hashCode$方法不用保证必须返回不同的值。但是,我们应该知道,在这种情况下,最好也设计成$hashCode$返回不同的值。因为,这样做有助于提高哈希表的性能。

实际情况下,$Object$类的$hashCode$方法在不同的对象确实返回了不同的哈希值。这通常是通过把对象的内部地址转换成一个整数来实现的。

这里所说的内部地址就是物理地址。需要注意的是,虽然$hashCode$值是依据它的物理地址而得来的。但是,不能说$hashCode$就代表对象的内存地址。

$hashCode$有什么用

在哈希表中,通过$key$计算出它的$hashCode$​值,再进行处理就可以确定它在哈希表中的位置,这样,在查询时,就可以直接定位到当前元素,提高查询效率。对于要插入的一个新元素,先去计算它的$hashCode$​值,如果此位置没有元素,那么就直接插入即可。如果此位置已经有值,可以通过$equals()$方法比较它们是否相等,不等则也插入到这个位置(可以用链表形式存放)。所以,$hashCode$提高了查询,插入元素的效率。

$equals$和$==$的区别

$equals$比较的是内容,而 == 比较的是地址。

$equals()$方法是定义在$Object$类中的。

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

可以看到,它的默认实现,就是 == ,用来比较两个对象的内存地址是否相等。如果一个对象不重写$equals$​,那么效果和 == 是一样的。

因此,在使用自定义类的对象时,如果要让两个对象的内容相同时认为对象时相同的,则需要重写$equals$方法。

为什么要重写$equals$和$hashCode$

前面已经说明为什么重写$equals$,重写$hashCode$就涉及到$Map$和$Set$(底层其实也是$Map$)的内容了。

以$hashMap$ $jdk1.8$的源码来看,如$put$方法。

image-20211203210055456

可以看到,在插入元素时,代码中多次进行$hash$​值的比较,只有当$hash$​值相等时,才会去比较$equals()$方法。当哈希值和$equals$比较都相等时,才会覆盖元素。$get$​方法也是如此。

只有当$hashCode$和$equals$都相等时,才认为是同一个元素。

重写$hashCode$和$equals$的目的,就是为了方便哈希表这样的结构快速的查询和插入,不重写,则无法比较元素,可能造成元素位置错乱。

重写了$equals$,就必须重写$hashCode$

在$JDK$​源码注释第二点就说明了这一点。如果不重写$hashCode$​,对于我们自定义的类,创建的两个内容相同的对象,将其中一个对象加入到$hashmap$中,另一个对象调用其$hashCode$查找位置仍能插入成功,这是不正确的。

还需要注意的是,讲对象插入到了$hashMap$后,不要在使用过程中,改变对象的值,这样会导致$hashCode$值发生改变,无法再获取到插入的值。

$String$类具有不可变性,所以我们经常使用$String$类作为$hashMap$的$key$​值。

$hashCode$相等,$equals$一定相等吗

显然不是。在源码中,当通过$hashCode$值处理后计算出来的位置相等(产生哈希碰撞)时,还需要比较它们的$equals$​,才能确定是否是同一个对象。因此,$hashCode$​相等时,$equals$不一定相等。

反过来,$equals$相等,那么$hashCode$一定相等吗?是一定的。$equals$都相等了,那么在$hashMap$中认为它们是同一个元素,那么$hashCode$值必须保证相等。

总结

$hashCode$相等,$equals$不一定相等

$hashCode$不等,$equals$一定不等

$equals$相等,$hashCode$一定相等

$equals$不等,$hashCode$不一定不等

在源码注释第三点也提到,当$equals$不等时。不必保证它们的$hashCode$不等。但是为了提高哈希表的效率,最好设计成不等。因为,我们既然知道它们不相等了,那么当 $hashCode$ 设计成不等时。只要比较$hashCode$ 不相等,我们就可以直接返回 $null$,而不必再去比较 $equals$了。这样,就减少了比较的次数,无疑提高了效率。

Author

叶润繁

Posted on

2021-11-25

Licensed under