写点什么

为什么重写 hashCode 一定也要重写 equals 方法?

  • 2024-08-23
    福建
  • 本文字数:2639 字

    阅读完需:约 9 分钟

这是一个经典的问题,我们先从==开始看起


==


"==" 是运算符


  1. 如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;


  1. 如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。


Person p1 = new Person("123");Person p2 = new Person("123");int a = 10;int b = 10;System.out.println(a == b);//trueSystem.out.println(p1 == p2); //显然不是同一个对象,false
复制代码


equals


作用是 用来判断两个对象是否相等。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:


public boolean equals(Object obj) {    return (this == obj);}
复制代码


equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址值。


一般情况下,类会重写 equals 方法用来比较两个对象的内容是否相等。比如 String 类中的 equals()是被重写了,比较的是对象的值。


hashcode


  1. hashcode 特性体现主要在它的查找效率上,O(1)的复杂度,在 Set 和 Map 这种使用哈希表结构存储数据的集合中。hashCode 方法的就大大体现了它的价值,主要用于在这些集合中确定对象在整个哈希表中存储的区域。


  1. 如果两个对象相同,则这两个对象的 equals 方法返回的值一定为 true,两个对象的 hashCode 方法返回的值也一定相同。(equals 相同,hashcode 一定相同,因为重写的 hashcode 就是计算属性的 hashcode 值)


  1. 如果两个对象返回的 HashCode 的值相同,但不能够说明这两个对象的 equals 方法返回的值就一定为 true,只能说明这两个对象在存储在哈希表中的同一个桶中。


只重写了 equals 方法,未重写 hashCode 方法


在 Java 中 equals 方法用于判断两个对象是否相等,而 HashCode 方法在 Java 中主要由于哈希算法中的寻域的功能(也就是寻找数据应该存储的区域的)。在类似于 set 和 map 集合的结构中,Java 为了提高在集合中查询匹配元素的效率问题,引入了哈希算法,通过 HashCode 方法得到对象的 hash 码,再通过 hash 码推算出数据应该存储的位置。然后再进行 equals 操作进行匹配,减少了比较次数,提高了效率。


public class Person {    String name;
public Person(String name) { this.name = name; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); }
public static void main(String[] args) { Person p1 = new Person("123"); Person p2 = new Person("123");
System.out.println(p1 == p2);//false System.out.println(p1.hashCode() == p2.hashCode());//false System.out.println(p1.equals(p2));//true
Set<Person> set = new HashSet<>(); set.add(p1); set.add(p2); System.out.println(set.size());//2 }}
复制代码


  • 当只重写了 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


@Overridepublic int hashCode() {    return Objects.hash(name);}
复制代码


重写了 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 值的对象来说的


  1. 只重写 equals 方法,不重写 hashCode 方法:


  • 有这样一个场景,有两个 Person 对象,可是如果没有重写 hashCode 方法只重写了 equals 方法,equals 方法认为如果两个对象的 name 相同则认为这两个对象相同。这对于 equals 判断对象相等是没问题的。


  • 对于 set 和 map 这类使用 hash 值的对象来说,由于没有重写 hashCode 方法,此时返回的 hash 值是不同的,因此不会去判断重写的 equals 方法,此时也就不会认为是相同的对象。


  1. 重写 hashCode 方法不重写 equals 方法


  • 不重写 equals 方法实际是调用 Object 方法中的 equals 方法,判断的是两个对象的堆内地址。而 hashCode 方法认为相等的两个对象在 equals 方法处并不相等。因此也不会认为是用一个对象


  • 因此重写 equals 方法时一定也要重写 hashCode 方法,重写 hashCode 方法时也应该重写 equals 方法。


总结


对于普通判断对象是否相等来说,只 equals 是可以完成需求的,但是如果使用 set,map 这种需要用到 hash 值的集合时,不重写 hashCode 方法,是无法满足需求的。尽管如此,也一般建议两者都要重写,几乎没有见过只重写一个的情况


文章转载自:seven97_top

原文链接:https://www.cnblogs.com/seven97-top/p/18374713

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
为什么重写hashCode一定也要重写equals方法?_Java_不在线第一只蜗牛_InfoQ写作社区