关于equals和hashCode方法
本篇文章记录学习$equals()$方法和$hashCode()$方法的内容。
什么是$hashCode$
通常所说的$hashCode$是一个经过哈希运算后的整型值,这个哈希运算的方法,定义在$Object$类中,通过一个本地方法$hashCode()$来实现(在$hashMap$中还会有一些其他的运算)。
1 | public native int hashCode(); |
要了解这个方法到底是什么作用,最有效的方法就是直接看源码注释。
返回当前对象的一个哈希值。这个方法用于支持一些哈希表,例如$hashMap$。
通常来讲,它有一下一些约定:
- 若对象的信息没有被修改,那么,在一个程序的执行期间,对于相同的对象,不管调用多少次$hashCode$方法,都应该返回相同的值。当然,在相同程序的不同执行期间,不需要保持结果的一致。
- 若两个对象的$equals()$方法返回值相同,那么,他们调用各自的$hashCode$方法时,也必须返回相同的结果。
- 当两个对象的$equals()$方法返回值不同时,那么他们的$hashCode$方法不用保证必须返回不同的值。但是,我们应该知道,在这种情况下,最好也设计成$hashCode$返回不同的值。因为,这样做有助于提高哈希表的性能。
实际情况下,$Object$类的$hashCode$方法在不同的对象确实返回了不同的哈希值。这通常是通过把对象的内部地址转换成一个整数来实现的。
这里所说的内部地址就是物理地址。需要注意的是,虽然$hashCode$值是依据它的物理地址而得来的。但是,不能说$hashCode$就代表对象的内存地址。
$hashCode$有什么用
在哈希表中,通过$key$计算出它的$hashCode$值,再进行处理就可以确定它在哈希表中的位置,这样,在查询时,就可以直接定位到当前元素,提高查询效率。对于要插入的一个新元素,先去计算它的$hashCode$值,如果此位置没有元素,那么就直接插入即可。如果此位置已经有值,可以通过$equals()$方法比较它们是否相等,不等则也插入到这个位置(可以用链表形式存放)。所以,$hashCode$提高了查询,插入元素的效率。
$equals$和$==$的区别
$equals$比较的是内容,而 == 比较的是地址。
$equals()$方法是定义在$Object$类中的。
1 | public boolean equals(Object obj) { |
可以看到,它的默认实现,就是 == ,用来比较两个对象的内存地址是否相等。如果一个对象不重写$equals$,那么效果和 == 是一样的。
因此,在使用自定义类的对象时,如果要让两个对象的内容相同时认为对象时相同的,则需要重写$equals$方法。
为什么要重写$equals$和$hashCode$
前面已经说明为什么重写$equals$,重写$hashCode$就涉及到$Map$和$Set$(底层其实也是$Map$)的内容了。
以$hashMap$ $jdk1.8$的源码来看,如$put$方法。
可以看到,在插入元素时,代码中多次进行$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$了。这样,就减少了比较的次数,无疑提高了效率。
关于equals和hashCode方法
https://twilight218.github.io/2021/11/25/关于equals和hashCode方法/