📜  Java中Hashcode方法的重要性

📅  最后修改于: 2022-05-13 01:54:36.386000             🧑  作者: Mango

Java中Hashcode方法的重要性

先决条件: Java中的 Equals() 和 hashCode() 方法

HashMap 和 HashSet 使用散列来操作数据。他们使用 hashCode() 方法来检查哈希值。 Object 类中 hashCode() 的默认实现为不同的对象返回不同的整数。有时,我们必须在程序中实现 hashCode 方法。
考虑以下示例

// Java puzzle to illustrate use
// of hashcode() and equals() method
import java.util.*;
public class Name {
    private final String first, last;
    public Name(String first, String last)
    {
        this.first = first;
        this.last = last;
    }
    public boolean equals(Object o)
    {
        if (!(o instanceof Name))
            return false;
        Name n = (Name)o;
        return n.first.equals(first) && n.last.equals(last);
    }
    public static void main(String[] args)
    {
        Set s = new HashSet();
        s.add(new Name("Shubham", "Juneja"));
        System.out.println(
            s.contains(new Name("Shubham", "Juneja")));
    }
}

输出:

false
    解释:
  • Name 实例由名字和姓氏组成。两个 Name 实例相等,由 equals 方法计算,如果它们的名字相等且姓氏相等。
  • 名字和姓氏使用 String 中定义的 equals 方法进行比较。如果两个字符串以相同的顺序由相同的字符组成,则它们是相等的。因此,如果两个 Name 实例表示相同的名称,则它们是相等的。例如,下面的方法调用返回true: new Name(“Shubham”, “Juneja”).equals(new Name(“Shubham”, “Juneja”)) 程序的main方法创建了两个Name实例,都代表Shubham朱尼娅。
  • 该程序将第一个实例放入一个哈希集中,然后检查该集合是否包含第二个。这两个 Name 实例是相等的,所以看起来程序应该打印 true。如果你运行它,它几乎肯定会打印错误。

    为什么没有预期的输出?

    • 错误是 Name 违反了 hashCode 合同。这可能看起来很奇怪,因为 Name 甚至没有 hashCode 方法,但这正是问题所在。 Name 类覆盖了 equals 方法,hashCode 契约要求相等的对象具有相等的哈希码。要履行此合同,您必须在覆盖 equals 时覆盖 hashCode。
    • 因为它无法覆盖 hashCode,所以 Name 类从 Object 继承了它的 hashCode 实现。此实现返回一个基于身份的哈希码。换句话说,不同的对象很可能具有不相等的哈希值,即使它们是相等的。 Name 不满足 hashCode 契约,因此包含 Name 元素的哈希集的行为是未指定的。
    • 当程序将第一个 Name 实例放入散列集时,该集将该实例的条目放入散列桶中。该集合根据实例的哈希值选择哈希桶,由其 hashCode 方法计算。当它检查第二个 Name 实例是否包含在哈希集中时,程序会根据第二个实例的哈希值选择要搜索的桶。因为第二个实例与第一个不同,它可能具有不同的哈希值。

    解决方案:

    • 如果两个哈希值映射到不同的桶,则 contains 方法将返回 false 。假设两个 Name 实例映射到同一个桶。我们知道的所有 HashSet 实现都有一个优化,其中每个条目除了元素本身之外还存储其元素的哈希值。当搜索一个元素时,实现选择适当的哈希桶并遍历其条目,将存储在每个条目中的哈希值与所需元素的哈希值进行比较。只有当两个哈希值相等时,实现才会检查元素是否相等。这种优化是有意义的,因为比较哈希码通常比比较元素便宜得多。
    • 因为这个优化,散列集在正确的桶中搜索是不够的;两个 Name 实例必须具有相等的散列值,以便散列集将它们识别为相等。因此,程序打印为 true 的几率是两个连续创建的对象具有相同身份哈希码的几率。
    • 结果可能会因使用的Java实现而异,但您极不可能在我们知道的任何 JRE 上看到该程序打印为 true。为了解决这个问题,s暗示向 Name 类添加一个适当的 hashCode 方法。添加此方法后,程序将按预期打印 true:
      public int hashCode() {
      return 63 * first.hashCode() + last.hashCode();
      }
      
        本文由Shubham Juneja 提供。如果您喜欢 GeeksforGeeks 并愿意做出贡献,您还可以使用contribute.geeksforgeeks.org 撰写文章或将您的文章邮寄至contribute@geeksforgeeks.org。在 GeeksforGeeks 主页上查看您的文章并帮助其他 Geeks。