如何确定哪些 Java 类型(例如 LocalDate)是可安全哈希的?

How to determine which Java types (e.g. LocalDate) are safely hashable?

背景:我想获取两组记录并根据唯一键对它们进行重复数据删除。所以我写了一个 class 可以专门用于这个目的(作为 Map 或 Set 中的键)。示例:

import lombok.Data;

@Data
public class Key {

  private final String email;
  private final LocalDate start;
  private final LocalDate end;
}

在这里,据我所知,lombok 应该生成 hashCode 方法来达到目的。但是,官方文档说明如下:

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of LocalDate may have unpredictable results and should be avoided. The equals method should be used for comparisons (reference).

现在,我的问题的第一部分是我是否应该担心这些“不可预测的结果”。我理解值类型和引用类型的区别,我想专门使用 LocalDate 因为它是值类型。这段文字只是试图警告 LocalDate 的消费者不要将其视为引用类型,还是不可预测的行为比这更深入?

这是问题的第二部分(将其视为奖励问题)。我做了一些现场测试,其中我在代表相同日期的 LocalDate 的不同实例上调用了 hashCode,它似乎表现得像值类型应该的那样,例如 LocalDate.parse("2022-01-01").hashCode() == LocalDate.parse("2022-01-01").hashCode()是真的。但我希望更有信心这将适用于一系列输入。确定 LocalDate 将按照我期望的方式行事的最佳方法是什么?有什么方法可以做到这一点而不是基本上循环一堆日期并检查 hashCode 相等性?

“身份哈希码”!=“哈希码”

您引用的段落讨论了 identity 哈希码,它由 System.identityHashCode 返回,如果 class 则由 hashCode 返回没有实现自己的 hashCode 方法。

LocalDate 是否实现了它自己的hashCode方法,所以你在这里完全不用担心。

想想如果 hashCode returns 两个相同的 LocalDate 的不同哈希码意味着什么 - 这打破了 hashCode 的一般合同!

If two objects are equal according to the equals method, then calling the hashCode method on each of the two objects must produce the same integer result.

补充说明:

LocalDate 是一个 value-based class,它有一些显着的特性。重要的是:

  • the class's methods treat instances as freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() produces no visible change in the behavior of the class's methods;
  • the class does not provide any instance creation mechanism that promises a unique identity on each method call — in particular, any factory method's contract must allow for the possibility that if two independently-produced instances are equal according to equals(), they may also be equal according to ==;

这就是为什么强烈建议您不要执行依赖于“它是哪个对象”(又名“身份”)的操作,例如 ==identityHashCode 和同步,据说这些操作在 value-based classes 上的行为可能会在未来的版本中改变。

// no guarantee as to what the result will be
LocalDate.of(2020, 2, 16) == LocalDate.of(2020, 2, 16)