一、简介

哈希码是对象内容的数字表示。

在 Java 中,我们可以使用几种不同的方法来获取对象的哈希码:

  • 对象.hashCode()
  • Objects.hashCode() – 在 Java 7 中引入
  • Objects.hash() – 在 Java 7 中引入

在本教程中,我们将介绍每种方法。首先,我们将从定义和基本示例开始。在了解了基本用法之后,我们将深入研究它们之间的差异以及这些差异可能产生的后果。

2. 基本用法

2.1. 对象.hashCode()

我们可以使用 Object.hashCode() 方法来检索对象的哈希码。它与 Objects.hashCode() 非常相似,只是如果我们的对象为 null 则不能使用它。

话虽如此,让我们对两个相同的 Double 对象调用 Object.hashCode()

Double valueOne = Double.valueOf(1.0012);
Double valueTwo = Double.valueOf(1.0012);
        
int hashCode1 = valueOne.hashCode();
int hashCode2 = valueTwo.hashCode();
        
assertEquals(hashCode1, hashCode2);

正如预期的那样,我们收到相同的哈希码。

相反,现在让我们对 null 对象调用 Object.hashCode() ,并期望抛出 NullPointerException

Double value = null;
value.hashCode();

2.2. 对象.hashCode()

Objects.hashCode() 是一个空安全方法,我们可以使用它来获取对象的哈希码。哈希码对于哈希表和 equals() 的正确实现是必需的。

JavaDoc中指定的哈希码的一般约定是:

  • 在应用程序的同一执行期间,每次调用未更改的对象时,返回的整数都相同
  • 对于根据 equals() 方法相等的两个对象,返回相同的 hashcode

尽管这不是必需的,但不同的对象尽可能返回不同的哈希码。

首先,我们对两个相同的字符串调用 Objects.hashCode()

String stringOne = "test";
String stringTwo = "test";
int hashCode1 = Objects.hashCode(stringOne);
int hashCode2 = Objects.hashCode(stringTwo);
        
assertEquals(hashCode1, hashCode2);

现在,我们期望返回的哈希码是相同的。

另一方面,如果我们向 Objects.hashCode() 提供 null ,我们将得到零:

String nullString = null;
int hashCode = Objects.hashCode(nullString);
assertEquals(0, hashCode);

2.3. 对象.hash()

与仅接受单个对象的 Objects.hashCode() 不同, Objects.hash() 可以接受一个或多个对象并为它们提供哈希码。在底层, hash() 方法的工作原理是将提供的对象放入数组中并对它们调用 Arrays.hashCode()如果我们只向 Objects.hash() 方法提供一个对象,则无法期望与在该对象上调用 Objects.hashCode() 获得相同的结果。

首先,让我们用两对相同的字符串调用 Objects.hash()

String strOne = "one";
String strTwo = "two";
String strOne2 = "one";
String strTwo2 = "two";
        
int hashCode1 = Objects.hash(strOne, strTwo);
int hashCode2 = Objects.hash(strOne2, strTwo2);
        
assertEquals(hashCode1, hashCode2);

接下来,让我们使用单个字符串调用 Objects.hash()Objects.hashCode()

String testString = "test string";
int hashCode1 = Objects.hash(testString);
int hashCode2 = Objects.hashCode(testString);
        
assertNotEquals(hashCode1, hashCode2);

正如预期的那样,返回的两个哈希码不匹配。

3. 主要差异

在上一节中,我们讨论了 Objects.hash()Objects.hashCode() 之间的关键区别。现在,让我们更深入地研究一下,以便了解一些后果。

如果我们需要重写类的 equals() 方法之一,那么正确重写 hashCode() 也至关重要。

让我们首先为我们的示例创建一个简单的 Player 类:

public class Player {
    private String firstName;
    private String lastName;
    private String position;

    // Standard getters/setters
}

3.1.多字段哈希码实现

让我们想象一下,我们的 Player 类在所有三个字段中被认为是唯一的: firstNamelastNameposition

话虽如此,让我们看看在 Java 7 之前我们如何实现 Player.hashCode()

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + firstName != null ? firstName.hashCode() : 0;
    result = 31 * result + lastName != null ? lastName.hashCode() : 0;
    result = 31 * result + position != null ? position.hashCode() : 0;
    return result;
}

由于 Objects.hashCode()Objects.hash() 都是在 Java 7 中引入的,因此我们必须在对每个字段调用 Object.hashCode() 之前显式检查 null

让我们确认我们可以在同一个对象上调用 hashCode() 两次并获得相同的结果,并且我们可以在相同的对象上调用它并获得相同的结果:

Player player = new Player("Eduardo", "Rodriguez", "Pitcher");
Player indenticalPlayer = new Player("Eduardo", "Rodriguez", "Pitcher");
        
int hashCode1 = player.hashCode();
int hashCode2 = player.hashCode();
int hashCode3 = indenticalPlayer.hashCode();
        
assertEquals(hashCode1, hashCode2);
assertEquals(hashCode1, hashCode3);

接下来,让我们看看如何利用 Objects.hashCode() 获得的空安全性来缩短这一时间:

int result = 17;
result = 31 * result + Objects.hashCode(firstName);
result = 31 * result + Objects.hashCode(lastName);
result = 31 * result + Objects.hashCode(position);
return result;

如果我们运行相同的单元测试,我们应该期望得到相同的结果。

因为我们的类依赖多个字段来确定相等性,所以让我们更进一步,使用 Objects.hash() 使我们的 hashCode() 方法非常简洁:

return Objects.hash(firstName, lastName, position);

在此更新之后,我们应该能够再次成功运行我们的单元测试。

3.2. Objects.hash() 详细信息

在幕后,当我们调用 Objects.hash() 时, 值被放置在一个数组中,然后在该数组上调用 Arrays.hashCode()

话虽如此,让我们创建一个 Player 并将其哈希码与 Arrays.hashCode() 与我们使用的值进行比较:

@Test
public void whenCallingHashCodeAndArraysHashCode_thenSameHashCodeReturned() {
    Player player = new Player("Bobby", "Dalbec", "First Base");
    int hashcode1 = player.hashCode();
    String[] playerInfo = {"Bobby", "Dalbec", "First Base"};
    int hashcode2 = Arrays.hashCode(playerInfo);
        
    assertEquals(hashcode1, hashcode2);
}

我们创建了一个 Player ,然后创建了一个 String[]。 然后我们在 Player 上调用 hashCode() 并在数组上使用 Arrays.hashCode() 并收到相同的 hashcode。

4。结论

在本文中,我们学习了如何以及何时使用 Object.hashCode()Objects.hashCode()Objects.hash() 。此外,我们还研究了它们之间的差异。

作为回顾,让我们快速总结一下它们的用法:

  • Object.hashCode() :用于获取单个非空对象的哈希码
  • Objects.hashCode() :用于获取可能为 null 的单个对象的哈希码
  • Objects.hash() :用于获取多个对象的哈希码

与往常一样,示例代码可以在 GitHub 上获取。