为什么重写 hashCode 一定也要重写 equals 方法?
这是一个经典的问题,我们先从==开始看起
==
"==" 是运算符
如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。
equals
作用是 用来判断两个对象是否相等。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:
equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址值。
一般情况下,类会重写 equals 方法用来比较两个对象的内容是否相等。比如 String 类中的 equals()是被重写了,比较的是对象的值。
hashcode
hashcode 特性体现主要在它的查找效率上,O(1)的复杂度,在 Set 和 Map 这种使用哈希表结构存储数据的集合中。hashCode 方法的就大大体现了它的价值,主要用于在这些集合中确定对象在整个哈希表中存储的区域。
如果两个对象相同,则这两个对象的 equals 方法返回的值一定为 true,两个对象的 hashCode 方法返回的值也一定相同。(equals 相同,hashcode 一定相同,因为重写的 hashcode 就是计算属性的 hashcode 值)
如果两个对象返回的 HashCode 的值相同,但不能够说明这两个对象的 equals 方法返回的值就一定为 true,只能说明这两个对象在存储在哈希表中的同一个桶中。
只重写了 equals 方法,未重写 hashCode 方法
在 Java 中 equals 方法用于判断两个对象是否相等,而 HashCode 方法在 Java 中主要由于哈希算法中的寻域的功能(也就是寻找数据应该存储的区域的)。在类似于 set 和 map 集合的结构中,Java 为了提高在集合中查询匹配元素的效率问题,引入了哈希算法,通过 HashCode 方法得到对象的 hash 码,再通过 hash 码推算出数据应该存储的位置。然后再进行 equals 操作进行匹配,减少了比较次数,提高了效率。
当只重写了 equals 方法,未重写 hashCode 方法时,equals 方法判断两个对象是否相等时,返回的是 true(第三个输出),这是因为我们重写 equals 方法时,是对属性的比较;但判断两个对象的 hashCode 值是否相等时,返回的是 false(第二个输出),在没有重写 hashCode 方法的情况下,调用的是 Object 的 hashCode 方法,返回的是本对象的 hashCode 值,两个对象不一样,因此 hashCode 值不一样。
在 set 和 map 中,首先判断两个对象的 hashCode 方法返回的值是否相等,如果相等然后再判断两个对象的 equals 方法,如果 hashCode 方法返回的值不相等,则直接会认为两个对象不相等,不进行 equals 方法的判断。因此在 set 添加对象时,因为 hashCode 值已经不一致,判断出 p1 和 p2 是两个对象,都会添加进 set 集合中,因此返回集合中数据个数为 2 (第四个输出)
重写 hashCode 方法:重写 hashcode 方法时,一般也是对属性值进行 hash
重写了 hashCode 后,其是对属性值的 hash,p1 和 p2 的属性值一致,因此 p1.hashCode() == p2.hashCode()为 true,再进行 equals 方法的判断也为 true,认为是一个对象,因此 set 集合中只有一个对象数据。
为什么重写 hashCode 一定也要重写 equals 方法?
如果两个对象的 hashCode 相同,它们是并不一定相同的,因为 equals 方法不相等而 hashCode 方法返回的值却有可能相同的,比如两个不同的对象 hash 到同一个桶中
hashCode 方法实际上是通过一种算法得到一个对象的 hash 码,这个 hash 码是用来确定该对象在哈希表中具体的存储区域的。返回的 hash 码是 int 类型的所以它的数值范围为 [-2147483648 - +2147483647] 之间的,而超过这个范围,实际会产生溢出,溢出之后的值实际在计算机中存的也是这个范围的。比如最大值 2147483647 + 1 之后并不是在计算机中不存储了,它实际在计算机中存储的是-2147483648。在 java 中 hash 码也是通过特定算法得到的,所以很难说在这个范围内情况下不会不产生相同的 hash 码的。也就是说常说的哈希碰撞,因此不同对象可能有相同的 hashCode 的返回值。
因此 equals 方法返回结果不相等,而 hashCode 方法返回的值却有可能相同!
为什么重写 equals 一定也要重写 hashCode 方法?
这个是针对 set 和 map 这类使用 hash 值的对象来说的
只重写 equals 方法,不重写 hashCode 方法:
有这样一个场景,有两个 Person 对象,可是如果没有重写 hashCode 方法只重写了 equals 方法,equals 方法认为如果两个对象的 name 相同则认为这两个对象相同。这对于 equals 判断对象相等是没问题的。
对于 set 和 map 这类使用 hash 值的对象来说,由于没有重写 hashCode 方法,此时返回的 hash 值是不同的,因此不会去判断重写的 equals 方法,此时也就不会认为是相同的对象。
重写 hashCode 方法不重写 equals 方法
不重写 equals 方法实际是调用 Object 方法中的 equals 方法,判断的是两个对象的堆内地址。而 hashCode 方法认为相等的两个对象在 equals 方法处并不相等。因此也不会认为是用一个对象
因此重写 equals 方法时一定也要重写 hashCode 方法,重写 hashCode 方法时也应该重写 equals 方法。
总结
对于普通判断对象是否相等来说,只 equals 是可以完成需求的,但是如果使用 set,map 这种需要用到 hash 值的集合时,不重写 hashCode 方法,是无法满足需求的。尽管如此,也一般建议两者都要重写,几乎没有见过只重写一个的情况
文章转载自:seven97_top
评论