我注意到一个包含奇数个特定字符的字符串,比如' b'有一个哈希值
kM+r
其中k
和r
是整数,M
是2的幂。例如,如果M
是2的幂(比如16),则调制M
后,以下所有字符串都会产生相同的值:
"b" hashCode("b") = 98, 98%16 = 2
"bbb" hashCode("bbb") = 97314, 97314%16 = 2
"bbbbb" hashCode("bbbbb") = 293521890, 293521890%16 = 2
...
如果我使用以下公式(reference)来调整哈希值,则所有上述字符串都会哈希到同一个桶中,这绝对是 NOT 我们想要的。
int bucket_id = (hashCode(str) & 0x7fffffff) % M;
我在这里做错了吗?
答案 0 :(得分:1)
通常,哈希表实现在分配存储桶之前对对象hashCode执行额外的转换。例如,这里是如何在OpenJDK 8 end iterator
中实现的:
java.util.HashMap
这使得分布更均匀。 Java-7使用了更复杂的转换,如下所示:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
似乎发现它不必要地复杂化,因为Java-8简化了它。
此外,确定存储桶只有int h = key.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
,其中hash(key) & (n-1)
是存储桶的数量。与大多数哈希表实现一样,桶的数量是2的幂,这样的公式很好。
最后,为了防止冲突(意外或故意),在Java 8中实现了一种新的算法,它在桶中创建了一个包含太多元素的二叉树(如果密钥是n
)。这使得搜索过度拥挤的存储空间Comparable
而不是O(log n)
。