唯一的hashCodes不足以避免冲突

有一个常见的误解,即如果您具有唯一的hashCode(),则不会发生冲突。 虽然唯一或几乎唯一的hashCodes很好,但这还不是故事的结局。

问题在于HashMap的大小不是无限的(或大小至少为2 ^ 32),这意味着hashCode()的数量必须减少为较小的位数。

HashMap以及HashSet和LinkedHashMap的工作方式是按以下方式对位进行突变:

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

然后为最低位应用掩码以选择存储区。 问题在于,即使像Integer一样使用唯一的hashCode(),也会有具有不同哈希码的值映射到同一存储桶。 您可以研究Integer.hashCode()的工作方式:

public static void main(String[] args) {
    Set integers = new HashSet<>();
    for (int i = 0; i <= 400; i++)
        if ((hash(i) & 0x1f) == 0)
            integers.add(i);
    Set integers2 = new HashSet<>();
    for (int i = 400; i >= 0; i--)
        if ((hash(i) & 0x1f) == 0)
            integers2.add(i);
    System.out.println(integers);
    System.out.println(integers2);

}

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);
}

打印:

[373, 343, 305, 275, 239, 205, 171, 137, 102, 68, 34, 0]
[0, 34, 68, 102, 137, 171, 205, 239, 275, 305, 343, 373]

条目的顺序与之相反,因为它们是作为HashMap的链接列表而添加的,将所有条目置于同一存储桶中。

解决方案?

一个简单的解决方案是让一个存储桶变成一棵树,而不是一个链表。 在Java 8中,它将对字符串键执行此操作,但是可以对所有可比较类型AFAIK执行此操作。

另一种方法是允许使用自定义哈希策略,以使开发人员避免此类问题,或者在每个集合的基础上随机化突变,从而将应用程序的成本摊销到应用程序中。

其他注意事项

我倾向于支持64位哈希码,尤其是支持复杂对象的哈希码。 哈希码本身发生冲突的可能性很小,并且很好地支持非常大的数据结构。 例如数十亿。


翻译自: https://www.javacodegeeks.com/2013/10/unique-hashcodes-is-not-enough-to-avoid-collisions.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值