我应该如何在hashCode()中将long映射到int?

Han*_*etz 47 java algorithm hash

我有一系列具有long字段的对象,其值唯一地标识整个系统中的特定对象,非常类似于GUID.我已经覆盖Object.equals()了使用这个id进行比较,因为我希望它能够处理对象的副本.现在我想要覆盖Object.hashCode(),这基本上意味着将我映射long到一些int返回值.

如果我理解hashCode正确的目的 ,它主要用在哈希表中,因此需要均匀分布.这意味着,只需返回id % 2^32就足够了.这就是全部,还是我应该注意别的什么?

Tof*_*eer 88

从Java 8开始,您就可以使用了

Long.hashCode(guid);
Run Code Online (Sandbox Code Playgroud)

对于旧版本的Java,您可以使用以下内容:

Long.valueOf(guid).hashCode();
Run Code Online (Sandbox Code Playgroud)

请注意,此解决方案为堆栈创建了一个新的Object,而第一个没有(尽管Java可能会优化对象创建...)

查看文档,两种方法都只使用以下算法:

(int)(this.longValue()^(this.longValue()>>>32))
Run Code Online (Sandbox Code Playgroud)

这些都是不错的解决方案,因为它们使用了Java库 - 总是更好地利用已经测试过的东西.

  • @Mark:如果与`-XX:+ DoEscapeAnalysis`标志一起使用,最近的Sun/Oracle JVM**将优化它.JVM将能够注意到`Long`实例不需要存在于语句之外,因此可以在堆栈上创建.此外,它将能够内联代码(它会注意到我们谈论的是`Long`而不是另一个扩展`Long`的类).基于堆栈的创建和内联允许JVM知道的完整主机进一步优化. (4认同)
  • 这可能是昂贵的,因为它需要创建对象(因此是番石榴替代品).至于算法本身,唯一的危险是当上下32位具有相关意义时.例如,对于"Point"类来说,这将是一个可怕的哈希码,它将32位x和y坐标存储在一个long中. (2认同)

Col*_*inD 9

如果你还没有使用Guava,这有点小事,但是Guava可以很好地为你做到这一点:

public int hashCode() {
  return Longs.hashCode(id);
}
Run Code Online (Sandbox Code Playgroud)

这给你相当于Long.valueOf(id).hashCode():

return (int) (value ^ (value >>> 32));
Run Code Online (Sandbox Code Playgroud)

另外,如果你有其他值或对象是哈希码的一部分,你可以写

return Objects.hashCode(longValue, somethingElse, ...);
Run Code Online (Sandbox Code Playgroud)

long会autoboxed成Long这样你会得到它正确的哈希码作为整个哈希码的一部分.


Gro*_*uez 5

你已经理解了hashCode正确的目的.是的,需要均匀分布(尽管不是实际要求).

我建议((id >> 32) ^ id).

以上表达式:

  • 使用原始值的所有位,不会预先丢弃任何信息.例如,根据您生成ID的方式,高位可能会更频繁地更改(或相反).
  • 不会对具有更多1(零)的值引入任何偏差,因为如果将两半与OR(AND)运算组合将是这种情况.

  • @Steve:在这种情况下,`>>>`和`>>`之间没有区别,因为在移位过程中引入的额外32位将被丢弃. (3认同)