我正在使用Java中的HashMap类。我的理解是哈希表的容量是桶数量的2倍(容量16表示4个桶)。当调用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()值拟合到桶中。
答案 0 :(得分:1)
该代码不适合" hashCode到桶,它只是"只是"传播哈希码,使高位更重要。这是该方法的javadoc。
计算key.hashCode()并将散列(XOR)更高的散列位降低。因为该表使用2次幂掩蔽,所以仅在当前掩码之上的位中变化的散列组将始终发生冲突。 (在已知的例子中是一组Float键,在小表中保存连续的整数。)因此我们应用一个向下传播高位比特影响的变换。速度,效用和比特扩展质量之间存在权衡。因为许多常见的哈希集合已经合理分布(因此不会受益于传播),并且因为我们使用树来处理容器中的大量冲突,所以我们只是以最便宜的方式对一些移位的位进行异或,以减少系统损失,以及结合最高位的影响,否则由于表格的限制而不会在索引计算中使用。
对桶的实际拟合是在getNode(int, Object)
方法中完成的:
first = tab[(n - 1) & hash]
其中hash
是hash(Object)
的结果,n
是哈希表的大小。
答案 1 :(得分:0)
这可能会有所帮助。如果我们要添加十个带有浮点键的项目(从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 |
从输出中我们可以发现,键的低位全为0,这导致所有项都分配给同一bin。但是执行右移和异或后,分布会变得更好。我认为情况就是在HashMap的hash()方法的源代码注释中描述的。