【问题标题】:hash() implementation in javajava中的hash()实现
【发布时间】:2016-12-03 17:12:28
【问题描述】:

我正在使用 Java 中的 HashMap 类。我的理解是哈希表的容量是桶数的 2 次方(容量 16 表示四个桶)。当调用 put(key,value) 时,key.hashCode() 输出一个整数,这个新添加的 (key,value) 对基于 key.hashCode()% 个桶放置。但下面是HashMap.class中的实际实现

 static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

从上面的代码中,我无法弄清楚 key.hashCode() 值是如何适合存储桶的。

【问题讨论】:

    标签: java collections hashmap buckets


    【解决方案1】:

    这可能会有所帮助。如果我们要添加 10 个带有从 1 到 10 的浮动键的项目。

            Map<Float, String> map = new HashMap<>();
            int numOfBuckets = 64; // HashMap will have 64 bins after inserting 10 items
    
            String format = "|%1$-5s|%2$-35s|%3$-25s|%4$-35s|%5$-25s|%6$-25s|\n";
            System.out.format(format, "i", "raw binary", "right-shifted 16 bits", "rehashed", "bucket before rehashed",
                    "bucket after rehashed");
    
            for (int i = 1; i <= 10; i++) {
                float key = i;
                int rawInt = Float.floatToRawIntBits(key);
                String binaryString = Long.toBinaryString(rawInt);
                String shifted16BitsString = Long.toBinaryString(rawInt >>> 16);
    
                int rehashed = rawInt ^ rawInt >>> 16;
                String rehashedString = Long.toBinaryString(rehashed);
    
                // HashMap calculates bin index with (n - 1) & hash
                String bucketBeforeRehashed = Long.toString((numOfBuckets - 1) & Objects.hashCode(key));
                String bucketAfterRehashed = Long.toString((numOfBuckets - 1) & rehashed);
    
                System.out.format(format, i, binaryString, shifted16BitsString, rehashedString,
                        bucketBeforeRehashed, bucketAfterRehashed);
                map.put(key, Integer.toString(i));
            }
    

    产生:

    |i    |raw binary                         |right-shifted 16 bits    |rehashed                           |bucket before rehashed   |bucket after rehashed    |
    |1    |111111100000000000000000000000     |11111110000000           |111111100000000011111110000000     |0                        |0                        |
    |2    |1000000000000000000000000000000    |100000000000000          |1000000000000000100000000000000    |0                        |0                        |
    |3    |1000000010000000000000000000000    |100000001000000          |1000000010000000100000001000000    |0                        |0                        |
    |4    |1000000100000000000000000000000    |100000010000000          |1000000100000000100000010000000    |0                        |0                        |
    |5    |1000000101000000000000000000000    |100000010100000          |1000000101000000100000010100000    |0                        |32                       |
    |6    |1000000110000000000000000000000    |100000011000000          |1000000110000000100000011000000    |0                        |0                        |
    |7    |1000000111000000000000000000000    |100000011100000          |1000000111000000100000011100000    |0                        |32                       |
    |8    |1000001000000000000000000000000    |100000100000000          |1000001000000000100000100000000    |0                        |0                        |
    |9    |1000001000100000000000000000000    |100000100010000          |1000001000100000100000100010000    |0                        |16                       |
    |10   |1000001001000000000000000000000    |100000100100000          |1000001001000000100000100100000    |0                        |32                       |
    

    我们可以从输出中发现,key 的低位都是 0,这导致所有 item 都被分配到同一个 bin。但是在执行右移和异或之后分布变得更好。我认为是 HashMap 的 hash() 方法中源代码的注释中描述的情况。

    【讨论】:

      【解决方案2】:

      该代码不“适合”哈希码到桶,它“只是”传播哈希码,使高位更重要。这里是那个方法的javadoc。

      计算 key.hashCode() 并将哈希的较高位传播(XOR)到较低位。由于该表使用二次幂掩码,因此仅在当前掩码之上位变化的散列集总是会发生冲突。 (已知的例子是在小表中保存连续整数的 Float 键集。)因此,我们应用了一种变换,将高位的影响向下传播。在位扩展的速度、实用性和质量之间存在折衷。因为许多常见的散列集已经合理分布(所以不要从传播中受益),并且因为我们使用树来处理 bin 中的大量冲突,我们只是以最便宜的方式对一些移位的位进行异或,以减少系统损失,以及合并最高位的影响,否则由于表边界,这些最高位将永远不会用于索引计算。

      对桶的实际拟合是在getNode(int, Object) 方法中完成的:

      first = tab[(n - 1) & hash]
      

      其中hashhash(Object) 的结果,n 是哈希表的大小。

      【讨论】:

      • 我之前也浏览过您附加的链接(在 hashmap.class 中)。您能否详细说明“它“只是”传播哈希码”部分。?
      • 我的猜测是,它是针对小型(
      • 好的,现在更清楚了。我猜当哈希值大于桶数时,(n-1) & hash 只会给你余数。
      猜你喜欢
      • 2015-05-18
      • 2011-05-13
      • 1970-01-01
      • 2012-04-20
      • 2010-10-17
      • 1970-01-01
      • 1970-01-01
      • 2012-11-07
      • 2016-07-28
      相关资源
      最近更新 更多