JavaRush /Java 博客 /Random-ZH /喝咖啡休息#168。为什么要重写 Java 中的 equals 和 hashcode 方法?

喝咖啡休息#168。为什么要重写 Java 中的 equals 和 hashcode 方法?

已在 Random-ZH 群组中发布

为什么要重写 Java 中的 equals 和 hashcode 方法?

来源: Medium 本文重点介绍两个密切相关的方法:equals()hashcode()。您将了解它们如何相互交互以及如何正确覆盖它们。 喝咖啡休息#168。 为什么要重写 Java 中的 equals 和 hashcode 方法? - 1

为什么我们要重写 equals() 方法?

在 Java 中,我们不能重载==+=-+等运算符的行为。他们按照给定的流程工作。例如,考虑==运算符的操作。

== 运算符如何工作?

它检查比较的两个引用是否指向内存中的同一实例。仅当两个引用表示内存中的同一实例时,==运算符才会计算为 true。我们看一下示例代码:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
假设在您的程序中,您在不同的位置创建了两个Person 对象并想要比较它们。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
从商业角度来看,两者看起来是一样的,对吧?但对于 JVM 来说它们并不相同。由于它们都是使用new关键字创建的,因此这些实例位于不同的内存段中。因此==运算符将返回false。但是如果我们不能重写==运算符,那么我们如何告诉 JVM 我们希望这两个对象得到相同的对待?这就是.equals()方法发挥作用的地方。您可以重写equals()来检查某些对象的某些字段是否具有相同的值,以便认为它们相等。您可以选择要比较的字段。如果我们说两个Person对象只有具有相同的年龄和相同的名称才是相同的,那么 IDE 将生成类似这样的内容 来自动创建equals() :
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
让我们回到之前的例子。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
是的,我们不能重载==运算符来按照我们想要的方式比较对象,但是 Java 为我们提供了另一种方式 - equals ()方法,我们可以根据需要重写该方法。 请记住,如果我们不在类中提供自定义版本的.equals()(也称为覆盖),则Object类中预定义的.equals()==运算符的行为将相同。 默认的equals()方法继承自Object,将检查正在比较的两个实例在内存中是否相同!

为什么我们要重写 hashCode() 方法?

Java 中的某些数据结构(例如HashSetHashMap)基于应用于这些元素的哈希函数来存储其元素。哈希函数是hashCode()。如果我们可以选择重写.equals()方法,那么我们也应该可以选择重写hashCode()方法。这是有原因的。毕竟,继承自Object的 hashCode() 的默认实现认为内存中的所有对象都是唯一的!但让我们回到这些哈希数据结构。这些数据结构有一个规则。 HashSet不能包含重复的值,HashMap不能包含重复的键。HashSet是使用HashMap实现的,每个HashSet值都作为键存储在HashMap中。HashMap是如何工作的?HashMap是一个具有多个段的原生数组。每个段都有一个链表(linkedList)。这个链表存储了我们的密钥。HashMap使用hashCode()方法为每个键找到正确的 linkedList ,然后迭代该linkedList的所有元素,并对每个元素应用equals()方法以检查该元素是否包含在其中。不允许有重复的密钥。当我们将某些内容放入HashMap时,键将存储在这些链表之一中。该键将存储在哪个链表中由该键的hashCode()方法的结果显示。也就是说,如果key1.hashCode()结果为 4,则key1将存储在现有LinkedList 数组的第 4 段中。默认情况下,hashCode()方法为每个实例返回不同的结果。如果我们有一个默认的equals(),其行为类似于==,将内存中的所有实例视为不同的对象,那么就不会有问题。您可能还记得,在前面的示例中,我们说过,如果Person实例的年龄和名称相同,则 我们希望它们被视为相等。 喝咖啡休息#168。 为什么要重写 Java 中的 equals 和 hashcode 方法? - 2
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
现在让我们创建一个映射来将这些实例存储为键,并以特定字符串作为值对。
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Person类中,我们没有重写hashCode方法,但是我们有一个重写的equals方法。由于默认hashCode对于person1.hashCode()person2.hashCode()的不同 Java 实例给出不同的结果,因此很有可能获得不同的结果。我们的地图可以以不同的链接列表中的不同的人结束。 这违背了HashMap 的喝咖啡休息#168。 为什么要重写 Java 中的 equals 和 hashcode 方法? - 3逻辑。毕竟,HashMap不能有多个相同的键! 重点是继承自Object类的默认hashCode()是不够的。即使我们重写了Person类的equals()方法。这就是为什么我们在重写equals方法之后必须重写hashCode()方法。现在让我们解决这个问题。我们需要重写hashCode()方法,以便它考虑与equals()相同的字段,即agename
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
hashCode()方法中,我们使用了一个简单的值(您可以使用任何其他值)。不过,建议使用质数来减少问题。让我们再次尝试将这些键存储在HashMap中:
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode()person2.hashCode()是相同的。假设它们是 0。HashMap 转到段 0,LinkedList在其中将person1保存为值为“1”的键。第二种情况,当HashMap再次到桶0存储key为“2”的person2时,会发现那里已经存在另一个与它相等的key。这样它将覆盖以前的密钥。并且只有关键的person2才会存在于我们的HashMap中。 这就是我们了解HashMap规则的工作原理的方式,该规则规定不能使用多个相同的键! 但是,请记住,不相等的实例可以具有相同的哈希码,并且相等的实例必须返回相同的哈希码。喝咖啡休息#168。 为什么要重写 Java 中的 equals 和 hashcode 方法? - 4
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION