Java中的hashCode()方法是如何实现的?

7

hashCode()是如何实现的?

我的假设是它使用对象内存位置作为初始数字(种子),然后运行哈希函数。然而,这并不是事实。

我也看过Hash : How does it work internally?,但它没有回答我的问题。

是的,我可以下载SDK,但在这样做之前,也许有人已经了解了它。

谢谢 :)

编辑: 我知道它应该被覆盖等,所以请试着保持主题 :)


1
从您自己的链接中:“这通常是通过将对象的内部地址转换为整数来实现的,但Java(TM)编程语言不要求使用此实现技术。” 当然,这取决于实现的自由裁量权,但这应该可以确认您对Oracle实现的假设。您为什么说这不是事实呢? - Mark Peters
如果您查看Object类,它具有自己的本地实现 -public native int hashCode(); - Subhrajyoti Majumder
@MarkPeters,当对象在内存中移动时会发生什么? - Adrian
5个回答

25
不,不,不。这个帖子中所有的答案都是错误的,或者至少只是部分正确的。
首先,Object.hashCode()是一个本地方法,因此它的实现完全取决于JVM。它可能在HotSpot和其他VM实现(如JRockitIBM J9)之间有所不同。
如果你正在问:

hashCode()在Java中是如何实现的?

那么答案是:它取决于你使用的VM。
假设你正在使用Oracle的默认JVM——即HotSpot,则我可以告诉你,HotSpot有六个hashCode()实现。你可以使用-XX:hashCode=n标志通过命令行运行JVM来选择它,其中n可以是:
0 – Park-Miller RNG (default)
1 – f(address, global_statement)
2 – constant 1
3 – Serial counter
4 – Object address
5 – Thread-local Xorshift

The above is copied from this post.
如果你深入研究HotSpot源代码,你可能会找到以下片段:
if (hashCode == 0) {
  value = os::random();
} else {
  ...

os::random()只是Park-Miller伪随机生成算法的实现。

就这样。 没有任何关于内存地址的概念。虽然另外两个实现,14,使用对象的内存地址,但默认的不使用它。
Object.hashCode()基于对象地址的概念在很大程度上是一个历史遗产——这已经不再是真实的了。


我知道在Object#hashCode()的JavaDoc中,我们可以阅读到:

(...) 这通常是通过将对象的内部地址转换为整数来实现的,但这种实现技术不是Java™编程语言所必需的。

但它已经过时且误导人。


5
当然,具体实现会有所不同,但通常一个对象的哈希码会惰性计算并存储在对象头中。为了保证头部较小,同时还要允许复杂的锁定算法,头部可能会采取一些奇怪的方法。
在OpenJDK/Oracle JVM中,计算初始哈希码的通常方法是基于第一次请求时的内存地址。由于对象在内存中移动,所以每次使用地址都不是一个好选择。哈希码并不是实际地址 - 实际地址通常是8的倍数,这对于直接在哈希表中使用尤其是使用2的幂大小并不是很好。请注意,身份哈希码并不唯一。
HotSpot有构建时选项,可始终使用零或使用安全随机数生成器(SRNG)进行测试。

8的倍数可以乘以一个质数,得到更好的可哈希数。 - Adrian
@Adrian 它仍然是8的倍数。实际上它只是被移位了。 - Tom Hawtin - tackline
1
你只是部分正确。确实,这是实现特定的。但通常方法基于内存地址是错误的。这是一个非常常见的错误 - 我不怪你,因为即使是Object#hashCode()的javadoc也会误导人。 - G. Demecki
历史上是的。现在不是 :) - G. Demecki

0

hashcode() 函数的实现因对象而异。如果您想知道特定类如何实现 hashcode(),则必须查找该类。


0

Object类定义的hashCode方法为不同的对象返回不同的整数。这可以通过将对象的内部地址转换为整数来实现(但此实现风格不是标准所必需的)。对于覆盖hashCode以支持哈希表(equal和hashCode)的新类,情况变得有趣: http://www.javapractices.com/topic/TopicAction.do?Id=28


-1

我假设你在谈论hashCodeObject实现,因为该方法可以和应该被重写。

它的实现取决于具体情况。对于Sun JDK而言,它是基于对象的内存地址。


1
我认为这有点误导。对象在内存中移动,但具有稳定的身份哈希码。 - Tom Hawtin - tackline
@TomHawtin-tackline 正是我问这个问题的原因;它看起来像一个好的种子(比如乘以一个质数),但是当你意识到对象在内存中被移动时... 我不知道。 - Adrian
由于两个原因而被踩。首先:通常没有必要覆盖hashCode。其次:Sun JDK的默认值是伪随机数,而不是对象的内存地址。您可以查看我的帖子了解一些信息。 - G. Demecki
@Adrian的哈希码是稳定的(尽管它是随机的),因为在第一次计算后,结果会被存储在对象的头部,并在后续对hashCode()的调用中返回。 - G. Demecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接