什么是 java 中一年和一个月元组的好哈希代码



在Java中,假设我有一长串年和月对。像 2018:03 有很多重复项。

Month will always be starting with 1.
Year will always be > Month, starting with 2010
if Month or Year == 0 [not_set], hashcode can return 0 (fine), I ignore them

我想遍历此列表,并从每个条目的这两个值创建一个哈希,以确定我是否已经有一个特定的组合。

通常我会为这样的条目创建一个对象,其中包含两个 int 成员并覆盖等于和哈希码,将它们全部添加到一个 Set 中。

我应该如何实现哈希代码?

据我记得有效的Java,我会写这样的东西:

@Override
public int hashCode() {
int hash = year;
hash = 31 * hash + month;
return hash;
}

但我认为,因为月份总是比年少,在这种情况下,我擅长:

@Override
public int hashCode() {
return year * month;
}

直到4020年,不应该发生任何碰撞。

你能想到还有更有效的方法来实现我的目标吗? 还是已经太晚了,我的头快要分崩离析了?

只要满足hashCode的一般契约,就应该是一个精细的哈希代码实现:

  • 每当在 Java 应用程序执行期间多次对同一对象调用它时,hashCode 方法必须始终如一 返回相同的整数,前提是 equals 中未使用任何信息 修改对象的比较。此整数不需要保留 从应用程序的一次执行到另一个执行的一致性 同一应用程序。
  • 如果根据 equals(Object) 方法两个对象相等,则在两个对象中的每一个上调用 hashCode 方法必须 生成相同的整数结果。
  • 如果根据 equals(java.lang.Object) 方法,两个对象不相等,则不需要调用 hashCode 方法 这两个对象中的每一个都必须生成不同的整数结果。 但是,程序员应该意识到,产生不同的 不相等对象的整数结果可能会提高 哈希表。

实现hashCode方法的另一种方法是调用Objects.hash

return Objects.hash(year, month);

正如你已经提到的Effective Java [1],这是从同一本书中制作一个好的哈希函数的秘诀:

  1. 声明一个名为 result 的 int 变量,并将其初始化为对象中第一个有效字段的哈希代码 c

  2. 对于对象中剩余的每个重要字段 f,请执行以下操作:

    a. 计算字段的整数哈希代码 c:

我。如果字段是基元类型,则计算 Type.hashCode(f),其中 Type 是对应于 f 类型的盒装基元类。

ii. 如果字段是对象引用,并且此类的 equals 方法通过递归调用 equals 来比较字段,则递归调用字段上的 hashCode。如果需要更复杂的比较,请计算此字段的"规范表示",并在规范表示上调用hashCode。如果字段的值为 null,请使用 0(或其他一些常量,但 0 是传统的)。

iii. 如果字段是一个数组,请将其视为每个重要元素都是一个单独的字段。也就是说,通过递归应用这些规则来计算每个重要元素的哈希代码,并按步骤 2.b 组合值。如果数组没有重要元素,请使用常量,最好不要 0。如果所有元素都很重要,请使用 Arrays.hashCode。

b. 将步骤 2.a 中计算的哈希代码 c 合并为结果,如下所示:

结果 = 31 * 结果 + c;

  1. 返回结果。

将此食谱翻译成您的食谱:

@Override
public int hashCode() {
int hash = Integer.hashCode(year);
hash = 31 * hash + Integer.hashCode(month);
return hash;
}

[1] Effective Java, Third Edition (http://www.informit.com/store/effective-java-9780134685991)

你可以看看Java 8的java.time.YearMonth代码(或者ThreenTen Backport中的等效类,用于Java <= 7)。两者都使用:

public int hashCode() {
return year ^ (month << 27);
}

最新更新