CodeGym/Java 博客/China/Java 中的哈希码是什么?
作者
Oleksandr Miadelets
Head of Developers Team at CodeGym

Java 中的哈希码是什么?

已在 China 群组中发布
个会员

哈希原理

首先,在定义 Java 哈希码之前,我们需要了解什么是哈希以及它的用途。哈希是对某些数据应用哈希函数的过程。哈希函数只是一个数学函数。不用担心不理解!“数学”并不总是意味着“复杂”。这里哈希仅仅表示我们有一些数据和某种规则,将数据映射到一组字符(代码)。 例如,它可以是十六进制密码。我们将一些任意大小的数据作为输入,并对其应用哈希函数。作为输出,我们得到一个固定大小的数据,比如 32 个字符。通常,这种函数会将一大段数据转换为一个小的整数值。该函数工作的结果称为哈希码。 哈希函数广泛用于密码学,还用于其他一些领域。哈希函数可以不同,但它们全都具有特定的属性:
  • 特定对象具有特定的哈希码。
  • 如果两个对象相等,则它们的哈希码相同。反过来则不正确。
  • 如果哈希码不同,则对象肯定不相等。
  • 不同的对象可能具有相同的哈希码。不过,这种事可能性极低。如果这样,则会发生冲突,这种情况可能会导致丢失数据。
“合适的”哈希函数会将冲突的概率降至最低。

Java 中的哈希码

在 Java 中,哈希函数通常关联 hashCode() 方法。准确地说,将哈希函数应用于 Object 的结果是哈希码。每个 Java 对象都有一个哈希码。一般来说,哈希码是一个由 Object 类的 hashCode() 方法计算出来的数字。通常,程序员会为其对象重写该方法以及与 hashCode() 相关的 equals() 方法,以便更高效地处理特定数据。 hashCode() 方法返回一个 int(4 字节)值,它是对象的数字表示。例如,集合使用此哈希码以更有效地存储数据,从而更快地进行访问。 默认情况下,对象的 hashCode() 函数返回存储对象的内存单元的编号。因此,如果未对应用程序代码进行任何更改,则该函数应返回相同的值。如果代码稍有变化,哈希码值也会发生变化。 Java 中使用的哈希码是什么?首先,Java 哈希码助力程序更快运行。例如,如果我们比较某种类型的两个对象 o1o2,则运算 o1.equals(o2) 所花时间大约是 o1.hashCode() == o2.hashCode() 的 20 倍。

Java equals()

在父类 Object 中,除了 hashCode() 方法之外,还有 equals(),即用于检查两个对象是否相等的函数。此函数的默认实现只是检查两个对象的链接是否相等。 equals()hashCode() 签有合约,所以如果你重写其中一个,则应该重写另外一个,以免破坏这个合约。

实现 hashCode() 方法

示例

现在创建一个类 Character,该类带有一个字段 name。之后,我们创建 Character 类的两个对象 character1character2 并将它们设置为相同的名称。如果我们使用 Object 类的默认 hashCode()equals() 方法,我们肯定会得到不同的,不相等的对象。这就是 Java 中的哈希码的工作原理。这两个对象将具有不同的哈希码,因为它们位于不同的内存单元中,equals() 操作结果将为 false。
import java.util.Objects;

public class Character {
    private String Name;

    public Character(String name) {
        Name = name;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public static void main(String[] args) {
        Character character1 = new Character("Arnold");
        System.out.println(character1.getName());
        System.out.println(character1.hashCode());
        Character character2 = new Character("Arnold");
        System.out.println(character2.getName());
        System.out.println(character2.hashCode());
        System.out.println(character2.equals(character1));
    }
}
运行程序所得的结果如下:

Arnold
1595428806
Arnold
1072408673
false
控制台中的两个 10 位数字是哈希码。如果我们想要拥有相同名称的相同对象怎么办?我们应该做什么? 答案是:我们应该为 Character 类重写 Object 类的 hashCode()equals() 方法。我们可以在 IDEA IDE 中自动完成,只需按键盘上的 alt + insert 并选择 Generate -> equals() and hashCode() 即可。 什么是 Java hashCode() - 2通过示例得到下列代码:
import java.util.Objects;

public class Character {
    private String Name;

    public Character(String name) {
        Name = name;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Character)) return false;

        Character character = (Character) o;

        return getName() != null ? getName().equals(character.getName()) : character.getName() == null;
    }

    @Override
    public int hashCode() {
        return getName() != null ? getName().hashCode() : 0;
    }

    public static void main(String[] args) {
        Character character1 = new Character("Arnold");
        System.out.println(character1.getName());
        System.out.println(character1.hashCode());
        Character character2 = new Character("Arnold");
        System.out.println(character2.getName());
        System.out.println(character2.hashCode());
        System.out.println(character2.equals(character1));
    }
}
运行此代码的结果:

Arnold
1969563338
Arnold
1969563338
true
所以现在程序将我们的对象识别为相等并且它们具有相同的哈希码。 Java 中的哈希码是什么? - 2

Java 哈希码示例:

你自己的 hashCode() 和 equals()

你还可以自行创建 equals()hashCode() 实现,但要小心并记住尽量减少哈希码冲突。下面是 Student 类中我们自己的 hashCode()equals() 方法的示例:
import java.util.Date;

public class Student {
   String surname;
   String name;
   String secondName;
   Long birthday; // Long instead of long is used by Gson/Jackson json parsers and various orm databases

   public Student(String surname, String name, String secondName, Date birthday ){
       this.surname = surname;
       this.name = name;
       this.secondName = secondName;
       this.birthday = birthday == null ? 0 : birthday.getTime();
   }
//Java hashcode example
   @Override
   public int hashCode(){
       //TODO: check for nulls
       //return surname.hashCode() ^ name.hashCode() ^ secondName.hashCode() ^ (birthday.hashCode());
       return (surname + name + secondName + birthday).hashCode();
   }
   @Override
   public boolean equals(Object other_) {
       Student other = (Student)other_;
       return (surname == null || surname.equals(other.surname) )
               && (name == null || name.equals(other.name))
               && (secondName == null || secondName.equals(other.secondName))
               && (birthday == null || birthday.equals(other.birthday));
   }
}
Main 类工作展示:
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;

public class Main {
   static HashMap<Student, Integer> cache = new HashMap<Student, Integer>(); // <person, targetPriority>

   public static void main(String[] args) {
       Student sarah1 = new Student("Sarah","Connor", "Jane", null);
       Student sarah2 = new Student("Sarah","Connor", "Jane", new Date(1970, 01-1, 01));
       Student sarah3 = new Student("Sarah","Connor", "Jane", new Date(1959, 02-1, 28)); // date not exists
       Student john = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
       Student johnny = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
       System.out.println(john.hashCode());
       System.out.println(johnny.hashCode());
       System.out.println(sarah1.hashCode());
       System.out.println();
       cache.put(sarah1, 1);
       cache.put(sarah2, 2);
       cache.put(sarah3, 3);
       System.out.println(new Date(sarah1.birthday));
       System.out.println();
       cache.put(john, 5);
       System.out.println(cache.get(john));
       System.out.println(cache.get(johnny));
       cache.put(johnny, 7);
       System.out.println(cache.get(john));
       System.out.println(cache.get(johnny));
   }
}

哈希码用途是什么?

首先,哈希码助力程序更快运行。例如,如果我们比较某种类型的两个对象 o1o2,则运算 o1.equals(o2) 所花时间大约是 o1.hashCode() == o2.hashCode() 的 20 倍。在 Java 中,哈希原理支持一些流行的集合,例如 HashMapHashSetHashTable

结论

每个 Java 对象都有从 Object 类继承的 hashCode()equals() 方法。要获得良好的工作平等机制,你最好为自己的类重写 hashcode()equals() 方法。使用哈希码助力程序更快运行
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论