理解奇怪的Java哈希函数

理解奇怪的Java哈希函数,java,hash,Java,Hash,以下是java.util.HashMap中哈希函数的源代码。这些评论很好地解释了它的成就但是怎么做?那些^和>操作员在做什么有人能解释一下代码实际上是如何执行注释所说的吗? /** * Applies a supplemental hash function to a given hashCode, which * defends against poor quality hash functions. This is critical * because HashMap uses po

以下是
java.util.HashMap
中哈希函数的源代码。这些评论很好地解释了它的成就但是怎么做?那些
^
>
操作员在做什么有人能解释一下代码实际上是如何执行注释所说的吗?

/**
 * Applies a supplemental hash function to a given hashCode, which
 * defends against poor quality hash functions.  This is critical
 * because HashMap uses power-of-two length hash tables, that
 * otherwise encounter collisions for hashCodes that do not differ
 * in lower bits. Note: Null keys always map to hash 0, thus index 0.
 */
static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

>
是一种具有零填充的位移位

^
是一个异或

XOR
也称为异或——它是一种组合两个数字的数学运算符。看


n
进行右位移位类似于从数字中删除
n
最低位。因此,如果数字是
00010111
,将其右移1,则得到
00001011

^
是,
>
是。

>
>
显示为无符号右位移位,
^
为位异或


它是按位异或和无符号右移的组合

有关更多说明,请参见此处:

这里是一个示例以及它们设计时的一些注意事项。虽然不太详细,但要点是:

操作必须使用一系列计算来实现雪崩。 雪崩意味着输入中的一点差异将产生 大约1/2的输出位可能不同


基本上,补充哈希函数的目标是删除输入中的任何规则,因为这些规则可能导致哈希表退化。

以下是一些代码和示例输出:

public static void main ( String[] args ) {
    int h = 0xffffffff;
    int h1 = h >>> 20;
    int h2 = h >>> 12;
    int h3 = h1 ^ h2;
    int h4 = h ^ h3;
    int h5 = h4 >>> 7;
    int h6 = h4 >>> 4;
    int h7 = h5 ^ h6;
    int h8 = h4 ^ h7;

    printBin ( h );
    printBin ( h1 );
    printBin ( h2 );
    printBin ( h3 );
    printBin ( h4 );
    printBin ( h5 );
    printBin ( h6 );
    printBin ( h7 );
    printBin ( h8 );

}

static void printBin ( int h ) {
    System.out.println ( String.format ( "%32s", 
        Integer.toBinaryString ( h ) ).replace ( ' ', '0' ) );
}
其中打印:

11111111111111111111111111111111
00000000000000000000111111111111
00000000000011111111111111111111
00000000000011111111000000000000
11111111111100000000111111111111
00000001111111111110000000011111
00001111111111110000000011111111
00001110000000001110000011100000
11110001111100001110111100011111
因此,代码将哈希函数分解为多个步骤,以便您可以看到发生了什么。第一次移位20个位置的异或与第二次移位12个位置的异或创建了一个掩码,该掩码可以翻转int的底部20位中的0或更多。因此,可以将一些随机性插入到底部位中,从而利用可能分布得更好的更高位。然后通过异或将其应用于原始值,以将该随机性添加到低位。第二次7位移位或4位移位创建了一个掩码,该掩码可以翻转底部28位中的0位或更多位,通过利用先前的xor(已经处理了低位的一些分布),将一些随机性再次带到低位和一些更重要的位。最终结果是通过散列值更平滑地分配位

由于java中的hashmap通过将散列与桶数相结合来计算桶索引,因此需要对散列值的低位进行均匀分布,以便将条目均匀地分布到每个桶中


至于证明这限制了碰撞次数的说法,我没有任何意见。此外,有关构建哈希函数的一些好信息,以及关于两个数字的异或为什么会导致结果中位的随机分布的一些详细信息,请参见。

他们还可以参见:有人能解释一下代码实际上是如何按照注释所说的做的吗?-更适合这种问题。Philwb,谢谢你的回答。我真的很想知道,为什么特别是20、12、7和4。他们如何衡量随机性。这里的大多数答案都说引入随机性等等。它是如何产生这种随机性的?为什么正确的换档位置必须是20,为什么不能是21或19?。你能解释一下吗?对不起,如果我遗漏了一些基本的东西。不幸的是,我不能说为什么选择这些特定的班次。你可以通过其他轮班实现随机性。从数学上讲,也许这些特定的位移导致了碰撞次数有界的说法。然而,你可能需要咨询一些数学方面比较新的人来进一步探讨这个问题。如果你真的找到了一个合理的答案,我很想听听。那么,简言之,这个散列的设计是因为值可能更好地分布在桶中?