Java HashMap检测冲突

时间:2010-08-11 05:14:56

标签: java collections hash collision-detection

有没有办法检测Java Hash-map中的冲突?任何人都可以指出可能发生大量碰撞的情况。当然,如果你覆盖一个对象的哈希码并简单地返回一个常量值,肯定会发生冲突。我不是在谈论那个。我想知道前面提到的其他所有情况都会发生大量的碰撞无需修改默认的哈希码实现。

3 个答案:

答案 0 :(得分:14)

我创建了一个项目来对这些事情进行基准测试:http://code.google.com/p/hashingbench/(对于带有链接,开放寻址和布隆过滤器的哈希表)。

除了密钥的hashCode()之外,你需要知道“涂抹”(或“加扰”,正如我在该项目中所称的那样)功能哈希表。从this list开始,HashMap的拖尾函数相当于:

public int scramble(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

因此,如果在HashMap中发生冲突,必要的足够的条件如下:scramble(k1.hashCode()) == scramble(k2.hashCode())如果 k1.hashCode() == k2.hashCode()(否则,拖尾/加扰函数不会 一个函数),这总是如此,所以 >,但不是发生碰撞的必要的条件。

编辑:实际上,上述必要且充分的条件应为compress(scramble(k1.hashCode())) == compress(scramble(k2.hashCode())) - compress函数采用整数并将其映射到{0, ..., N-1},其中N是桶的数量,因此它基本上选择一个桶。通常,这只是实现为hash % N,或者当哈希表大小是2的幂(并且实际上是具有两个哈希表大小的幂的动机)时,hash & N(更快)。 (“compress”是Goodrich和Tamassia用于描述此步骤的名称,在Data Structures and Algorithms in Java中)。感谢ILMTitan发现我的邋。。

其他哈希表实现(ConcurrentHashMap,IdentityHashMap等)有其他需求并使用另一个拖尾/加扰函数,因此您需要知道您正在谈论哪一个。

(例如,HashMap的拖尾函数已经到位,因为人们使用具有最差类型hashCode()的HashMap用于HashMap的旧的,两个表的实现而没有拖尾 - 不同的对象在用于选择存储桶的低位中很少或根本没有 - 例如new Integer(1 * 1024)new Integer(2 * 1024) *等。正如您所看到的,HashMap的拖尾函数尽力而为所有位会影响低位)。

但是,所有这些都适用于常见情况 - 特殊情况是继承系统的hashCode()的对象。

PS:实际上,提示实现者插入拖尾函数的绝对丑陋的案例是Floats / Doubles的hashCode(),以及作为值的键的用法:1.0,2.0,3.0,4.0 ......,全部它们具有相同(零)低位。这是相关的旧错误报告:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4669519

答案 1 :(得分:3)

简单示例:哈希Long。显然,有64位输入,只有32位输出。 Long的哈希记录为:

(int)(this.longValue()^(this.longValue()>>>32))

即。想象一下,两个int值彼此相邻,并将它们异或。

因此所有这些都将具有0的哈希码:

0
1L | (1L << 32)
2L | (2L << 32)
3L | (3L << 32)

我不知道这是否算是“大量碰撞”,但这是碰撞易于制造的一个例子。

显然任何哈希,其中有超过2个 32 可能的值会发生冲突,但在很多情况下它们更难产生。例如,虽然我确实只使用ASCII值在String上看到了哈希冲突,但它们的生成比上面的更难。

答案 2 :(得分:1)

另外两个答案我看到一个很好的IMO,但我只是想分享一下,测试你的hashCode()HashMap中的行为表现如何以实际生成大量对象的最佳方法你的类,把它们放在特定的HashMap实现中作为密钥并测试CPU和内存负载。一百或两百万个条目是一个很好的数字,但如果您使用预期的地图大小进行测试,您将获得最佳结果。

我只是看了一个我怀疑它的散列函数的类。所以我决定使用该类型的随机对象填充HashMap并测试碰撞次数。我测试了两个正在调查的类的hashCode()实现。所以我在groovy中编写了你在底部看到的类,扩展了HashMap的openjdk实现,以计算HashMap中的冲突数量(参见countCollidingEntries())。请注意,这些不是整个哈希的真实冲突,而是包含条目的数组中的冲突。数组索引计算为hash & (length-1),这意味着该数组的大小越短,您获得的冲突就越多。此数组的大小取决于initialCapacity的{​​{1}}和loadFactor(当HashMap更多数据时,它会增加。

最后虽然我认为看这些数字没什么意义。使用错误的put()方法,HashMap速度较慢这一事实意味着只需通过对Map中数据的插入和检索进行基准测试,您就可以有效地了解哪个hashCode()实现更好。

hashCode()