Object.hashCode() 算法

Posted

技术标签:

【中文标题】Object.hashCode() 算法【英文标题】:Object.hashCode() algorithm 【发布时间】:2013-08-01 09:33:52 【问题描述】:

我在找Object.hashCode()的算法。

此代码是 Object.java 中的本机代码。

这是因为

(a) 代码在汇编中—— 从来没有在 Java 或任何其他 HLL 中使用过

(b) 根本没有公开

在任何一种情况下,我都希望掌握“如何计算 hashCode()”的算法(伪代码或一些详细解释)——其中的参数是什么计算和计算本身?

请注意:我要找的是hashCode() of Object-- 不同于 StringhashMap/table

//=============================================== ==============================

new Java docs--jdk 8 现在说

"The value returned by hashCode() is the object's hash code, which is the object's memory address in hexadecimal." 

【问题讨论】:

我相信它是原生的,因为Object的hashCode是一个内存地址标识符,实际上并不是对象的各个字段的hash。 有人需要更新您引用的文档。因为这是完全错误的。 【参考方案1】:

本机hashCode 方法实现依赖于JVM。 HotSpot默认返回随机数,可以在source code(函数get_next_hash)中查看

【讨论】:

【参考方案2】:

尽管有 Javadoc,但算法只能使用地址作为输入。这意味着即使新对象在伊甸园空间中使用相同的地址,它们也不会具有相同的 hashCode。

它可能正在使用许多算法,但并非所有算法都使用该地址。

注意:hashCode() 是 31 位的。

顺便说一句,您可以使用Unsafe.putInt(object, 1, value)on Hotspot 进行设置。

Set<Integer> ints = new LinkedHashSet<>();
int negative = 0, nonneg = 0;
for (int i = 0; i < 100; i++) 
    System.gc();
    for (int j = 0; j < 100; j++) 
        int h = new Object().hashCode();
        ints.add(h);
        if (h < 0) negative++;
        else nonneg++;
    

System.out.println("unique: " + ints.size() + " negative: " + negative + " non-neg: " + nonneg);

打印

unique: 10000 negative: 0 non-neg: 10000

使用不安全

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

Object o = new Object();
System.out.println("From header " + Integer.toHexString(unsafe.getInt(o, 1L)));
// sets the hashCode lazily
System.out.println("o.hashCode()  " + Integer.toHexString(o.hashCode()));
// it's here now.
System.out.println("after hashCode() From header " + Integer.toHexString(unsafe.getInt(o, 1L)));
unsafe.putInt(o, 1L, 0x12345678);
System.out.println("after change o.hashCode()  " + Integer.toHexString(o.hashCode()));

打印

From header 0
o.hashCode()  2260e277
after hashCode() From header 2260e277
after change o.hashCode()  12345678

【讨论】:

“hashCode() 仅使用对象的地址”将与 docjar.com/docs/api/java/lang/Object.html#hashCode 的规范一致。但是你说“即使新对象使用相同的地址,它们也不会具有相同的 hashCode”。那么这条领带是怎么破的呢?除了对象的引用地址之外的其他一些内存位置(比如某个成员的地址、它使用的结束地址/内存量、时间戳上的一些东西......?) 虽然满足“在一次执行中每次调用 hashCode() 时相同对象的相同 hashCode”,但仅此计算的对象地址感觉不太正确——与内存空间太一致,对哈希码的随机性没有那么强。 它将是一个不可变的“句柄”标识符,在对象的生命周期内保持不变。很可能依赖于 VM 实现,并且可能与内存地址没有任何关系,因为由于 GC 操作,java 中的所有对象都可以在物理内存中重定位。 @peterk 我相信在一个非常早期的版本中,无法移动对象,并且地址确实是哈希的一部分。 (Java 1.2 之前) 可能在过去,因为它很容易实现,但今天肯定不是这样或正确的。唯一合适的假设是它在对象的生命周期内是不变的,并且在 VM 实例中的所有对象中是唯一的。【参考方案3】:

hashCode 是本地方法,表示内部调用了系统库。这是因为哈希码在内部会尝试根据对象内存位置生成一个数字。此代码与机器相关,可能是用 C 编写的。

但如果你真的有兴趣查看原生代码,那么请按照以下步骤操作:

http://hg.openjdk.java.net/jdk7/jdk7-gate/jdk/file/e947a98ea3c1/src/share/native/java/

【讨论】:

嗯,就是这样——对象内存位置是唯一进入 hashCode() 计算的参数吗?它是怎么做的——可能在地址的低端位上?【参考方案4】:

这是因为它依赖于不暴露给 Java 代码的低级细节。标准库的一些基本部分(如java.lang.Object)必须在本机代码中实现。

顺便说一句,您至少可以找到一个interesting article,它更详细地介绍了 HotSpot 实现。

【讨论】:

以上是关于Object.hashCode() 算法的主要内容,如果未能解决你的问题,请参考以下文章

Object.toString 或 Object.hashCode 是不是曾经给出对象的内存地址

Object.hashCode()与Object.equals()

java核心技术-Object hashCode

三分钟读懂摘要算法

findBugs分类介绍

Java 分析:java.lang.Object.hashCode 占用一半的 CPU 时间但从未显式调用