繁体   English   中英

IdentityHashMap.hash() 中这段代码的用途是什么?

[英]What is the purpose of this code in IdentityHashMap.hash()?

/**
 * Returns index for Object x.
 */
private static int hash(Object x, int length) {
    int h = System.identityHashCode(x);
    // Multiply by -127, and left-shift to use least bit as part of hash
    return ((h << 1) - (h << 8)) & (length - 1);
}

来自: jdk/IdentityHashMap.java 在 jdk8-b120 · openjdk/jdk · GitHub

理论上System.identityHashCode()返回的 hash 值已经是均匀分布的了,那为什么还要多一个移位运算而不是直接length - 1的 AND 运算呢?

该实现似乎保证最低位为 0 以确保计算结果为偶数,因为该实现要求所有键都在偶数索引上,所有值都在奇数索引上。

h << 8似乎混合了低位和高位来处理当 System.identityHashCode() 被实现为 memory 地址或递增值时的情况,目前尚不清楚为什么这里只移动 8 位而不是HashMap.hash()也移动 16 位。

代码中的注释说:

hash table, as described for example in texts by Sedgewick and Knuth. The array alternates holding keys and values." “实施说明:这是一个简单的hash 表,如 Sedgewick 和 Knuth 的文本中的示例所述。数组交替保存键和值。”

事实上, hash方法返回一个值,该值用作数组的直接索引。 例如:

public V get(Object key) {
    Object k = maskNull(key);
    Object[] tab = table;
    int len = tab.length;
    int i = hash(k, len);
    while (true) {
        Object item = tab[i];
        if (item == k)
            return (V) tab[i + 1];
        if (item == null)
            return null;
        i = nextKeyIndex(i, len);
    }
}

这意味着hash需要返回偶数。 hash中的计算确保索引是均匀的,而不会丢弃System.identityHashCode(x)值的底部位。

为什么不扔掉最底层的部分呢?

好吧,答案在于System.identityHashCode的实现方式。 实际上,有多种算法可以生成 hash,所使用的算法(在运行时)取决于一个模糊的 JVM 命令行选项。

  • 一些算法(名义上)均匀分布在int的范围内。 对于那些人,丢弃底部位就可以了。

  • 其他算法不是这样的。 其中一种算法使用简单的全局计数器。 另一个使用对象的 memory 地址,删除了低 3 位。 如果选择这些算法,丢弃 LSB 会增加IdentityHashMap中发生 hash 次冲突的概率。

有关IdentityHashcode算法及其选择方式的更多信息,请参见https://shipilev.net/jvm/anatomy-quarks/26-identity-hash-code/ 请注意,JVM 行为的这一方面是未指定的,并且可能是特定于版本的。

我对这里发生的事情的预感是它旨在解决两个问题。

首先,这个 function 产生的槽索引必须是偶数。 (该实现将键存储在偶数表槽中,将值存储在奇数表槽中。)这意味着无论返回什么索引,其最后一位都必须为零。

其次,使用的标识 hash 代码(可能)基于 memory 地址,memory 地址的低位比高位“更随机”。 例如,如果我们分配一个对象列表,并且分配器将它们全部连续放置在 memory 中,那么它们的地址将具有相同的高位但低位不同。 (或者也许只有一个全局对象计数器在创建 object 时递增。在这种情况下,object 哈希的低位将同样比高位具有更广泛的分散。)

为了确保表格中的内容分散,我们因此希望将 hash 代码的低位与 hash 代码的“高”位“混合”。 减去h << 8的效果是将标识 hash 代码的低位向上移动,翻转它们,然后将它们添加回 hash 代码,在加法运算时引起一堆“涟漪”。 我认为(?)这是一种有效的方法,可以将更高熵的低位注入高位,一旦表开始变得越来越大,就可以在槽阵列上提供更均匀的 hash。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM