Java 中的hashCode()方法

原文:https://www.studytonight.com/java-examples/hashcode-method-in-java

哈希是为给定密钥生成唯一值的技术。哈希用于实现HashMap,这些数据结构提供了一种更快、更有效的数据查找方式。哈希被许多不同的集合使用,比如HashMapHashSet。hashCode()方法为给定的键返回一个整数 (也称为哈希或哈希值)。

Java 等于()方法

在继续之前,我们必须了解 equals()方法。equals()方法是一个简单的方法,如果被比较的两个对象相等,返回 true,否则返回 false

  • equals()方法主要由数据结构用来检查数据结构中是否存在值。这是通过将密钥与数据结构中的所有元素进行比较来完成的。
  • HashCode()方法通常是对具有 equals()方法的类的添加。增加 hashCode()方法是为了减少 equals()方法所做的比较次数。
  • 如果 equals()方法改变了,那么相应的 hashCode()方法也应该改变。
  • 例如,考虑一个有学生名字和 GPA 的学生班级。如果我们说如果两个学生的名字相同,那么他们就是平等的,那么 HashCode()方法也应该只关注学生的名字。
  • 基本上 hashCode()不应该考虑 equals()方法没有用于相等检查的任何字段。

equals()方法的示例如下所示。

public class HashDemo
{
    public static void main(String args[])
    {
        String str1 = "String 1";
        String str2 = "String 2";
        String str3 = "String 1";
        System.out.println("String 1 is equal to String 2: " + str1.equals(str2));
        System.out.println("String 1 is equal to String 1: " + str1.equals(str3));
    }
}

字符串 1 等于字符串 2:假 字符串 1 等于字符串 1:真

hashCode()方法

如上所述,hashCode()方法为给定的输入返回一个整数值。不同的 ide 都有自己的 hashCode()方法的实现,我们可以直接使用或者覆盖它们。任何 hashCode()方法都应该满足以下三个条件。这些条件合在一起也被称为哈希码契约

  • hashCode()方法必须在每次时为特定对象返回相同的值,假设我们没有更改 hashCode()方法或 equals()方法的实现,并且该对象没有以任何方式被修改。
  • 如果 equals()方法为两个对象返回 true,那么 hashCode()方法必须为两个对象返回相同的哈希值
  • hashCode()可以根据 equals()方法为两个不相等的对象返回相同的哈希值。

即使 hashCode()方法对于不同的输入返回相同的哈希值是完全有效的,但是这种方法在HashMap的实现中使用时会导致大量的冲突。在这种情况下,HashMap的查找时间将不是常数(0(1))。有一些方法可以处理这些碰撞,比如探测链接,但是避免它们是一个更好的选择。避免冲突的最好方法是编写一个强大的 hashCode()方法。

内置 hashCode()方法的示例

下面的代码演示了 Eclipse IDE 的内置 hashCode()方法的工作原理。

public class HashDemo
{
    public static void main(String args[])
    {
        String str1 = "String 1";
        String str2 = "String 1";
        String str3 = "String 3";

        System.out.println("Hash of Str1: " + str1.hashCode());
        System.out.println("Hash of Str2: " + str2.hashCode());
        System.out.println("String 1 is equal to String 1: " + str1.equals(str2));
        System.out.println("Hash of Str3: " + str3.hashCode());
        System.out.println("String 1 is equal to String 3: " + str1.equals(str3));
    }
}

str 1 的哈希:1859651586 str 2 的哈希:1859651586 字符串 1 等于字符串 1:真 str 3 的哈希:1859651588 字符串 1 等于字符串 3:假

HashMap的工作

HashMap是一种基于数据哈希值将数据存储在不同中的数据结构。这样当我们需要搜索一个元素时,我们可以直接去那个特定的桶,而不需要搜索每个桶HashMap的基本工作总结如下。

  • 当添加一个元素时,它的哈希值是通过使用 hashCode()函数生成的。
  • 这个哈希值决定了元素应该添加到的桶。如果 hashCode()方法不是很强大,并且为不同的元素生成重复的值,那么在一个桶中会出现多个元素。
  • 搜索元素时,首先为元素生成 hashCode(),我们直接在应该出现的桶中搜索。如果桶中存在多个对象,那么我们可以使用 equals()方法在桶中搜索正确的元素。

示例:实现 hashCode()方法

让我们尝试创建一个学生类,并实现我们自己的 hashCode()和 equals()方法。首先,我们将实现一个基本的 hashCode()方法,它只根据学生名字的第一个字母返回一个整数。

class Student
{
    String name;
    int regNo;
    double gpa;

    Student(String name, int regNo, double gpa)
    {
        this.name = name;
        this.regNo = regNo;
        this.gpa = gpa;
    }

    @Override
    public int hashCode()
    {
        return (int)this.name.charAt(0) - 64;
    }

    @Override
    public boolean equals(Object obj)
    {
        if(obj == null)
            return false;
        if(this == obj)
            return true;
        if(obj.getClass() != this.getClass())
            return false;
        Student s = (Student)obj;
        return this.name == s.name &&
               this.regNo == s.regNo && 
               this.gpa == s.gpa;
    }
}

public class HashDemo
{
    public static void main(String args[])
    {
        Student s1 = new Student("Justin", 59, 7.6);
        Student s2 = new Student("Jessica", 21, 8.0);
        Student s3 = new Student("Paul", 20, 5.5);
        Student s4 = new Student("Justin", 59, 7.6);

        System.out.println("Hash of s1: " + s1.hashCode());
        System.out.println("Hash of s2: " + s2.hashCode());
        System.out.println("Hash of s3: " + s3.hashCode());

        System.out.println("s1 is the same as s2: " + s1.equals(s2));
        System.out.println("s1 is the same as s4: " + s1.equals(s4));

    }
}

S1:10 的哈希 s2:10 的哈希 S3:16 的哈希 s1 与 S2 相同:假 s1 与 s4 相同:真

上面的 hashCode()方法是完全有效的,因为它满足合同的所有条件,但是它不是很强,因为名字第一个字母相同的不同学生将被分配相同的哈希值。让我们使用类的其他字段来编写一个更强的 hashCode()方法。

class Student
{
    String name;
    int regNo;
    double gpa;

    Student(String name, int regNo, double gpa)
    {
        this.name = name;
        this.regNo = regNo;
        this.gpa = gpa;
    }

    @Override
    public int hashCode()
    {
        return ((int)this.name.charAt(0) - 64) * this.regNo * (int)this.gpa;
    }

    @Override
    public boolean equals(Object obj)
    {
        if(obj == null)
            return false;
        if(this == obj)
            return true;
        if(obj.getClass() != this.getClass())
            return false;
        Student s = (Student)obj;
        return this.name == s.name &&
               this.regNo == s.regNo && 
               this.gpa == s.gpa;
    }
}

public class HashDemo
{
    public static void main(String args[])
    {
        Student s1 = new Student("Justin", 59, 7.6);
        Student s2 = new Student("Jessica", 21, 8.0);
        Student s3 = new Student("Paul", 20, 5.5);
        Student s4 = new Student("Justin", 59, 7.6);

        System.out.println("Hash of s1: " + s1.hashCode());
        System.out.println("Hash of s2: " + s2.hashCode());
        System.out.println("Hash of s3: " + s3.hashCode());
        System.out.println("Hash of s4: " + s4.hashCode());

        System.out.println("s1 is the same as s2: " + s1.equals(s2));
        System.out.println("s1 is the same as s4: " + s1.equals(s4));

    }
}

S1:4130 的哈希 s2:1680 的哈希 S3:1600 的哈希 s4:4130 的哈希 s1 与 S2 相同:假 s1 与 S4 相同:真

示例:在实现中使用内置的 hashCode()方法

上面的 hashCode()方法比上一个好。现在让我们在 hashCode()方法的实现中使用内置的 hashCode()方法。

@Override
public int hashCode()
{
    return this.name.hashCode() * this.regNo * (int)this.gpa;
}

常见问题

问:为什么在一些 hashCode()方法中使用数字 31?

31 是奇数。如果使用偶数,那么它将导致溢出,因为乘以 2 等于移位。现代虚拟机正在通过移位和减法取代 31 的使用。

问:如果我们知道原始密钥的哈希值,我们能拿回它吗?

不,任何哈希函数都不应该是可逆的。执行哈希运算是为了将大数据转换成一组固定的整数。多个密钥可以获得相同的哈希值,因此不可能获得原始密钥。

问:如果我们覆盖或更改 equals()方法,而不是 hashCode()方法,会发生什么?

如果我们覆盖 equals()方法,但是 hashCode()方法没有改变,那么 equals()方法用来检查两个对象是否相等的参数可能不会被 hashCode()方法使用。这会导致两个相等的对象具有不同的哈希值,这违反了 hashCode 契约的条件。

摘要

哈希是一种重要的技术,在许多不同的集合中使用它来提高查找时间。hashCode()方法用于生成这些数据结构所需的哈希值。hashCode()方法必须满足契约的三个条件。但是,强 hashCode()方法不应该为不同的对象返回相同的值,因为这会导致冲突。