一、简介
哈希码是对象内容的数字表示。
在 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 类在所有三个字段中被认为是唯一的: firstName 、 lastName 和 position 。
话虽如此,让我们看看在 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 上获取。