哈希表哈希避免负哈希码
Posted
技术标签:
【中文标题】哈希表哈希避免负哈希码【英文标题】:Hashtable hashing avoid negative hashcode 【发布时间】:2012-09-16 00:00:55 【问题描述】:我想知道为什么Hashtable 避免使用负哈希码?
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
(hash & 0x7FFFFFFF)
使有符号位从 0 变为正数,但为什么我们不能将有符号的 32 位整数视为无符号?甚至使用模块化技巧使其变得积极。例如,
public static long int_mod(int hashcode, int tab_length)
return (hashcode % tab_length + tab_length) % tab_length;
【问题讨论】:
我认为这种方法简单有效。可能这就是它被使用的原因。(hash & 0x7FFFFFFF)
缩小到正,% tab.length
缩小到标签大小。简单干净容易。
你指的是哪种方法?最初的实现?
是的。已经实现了。
整数除法和取模是迄今为止最慢的操作(在当代 Intel/AMD CPU 上可能是 40 个周期),而 &
属于最便宜的操作集(1 个周期,可以并行执行) .因此,您的解决方案将花费大约两倍于原始解决方案的时间。
【参考方案1】:
该值必须介于0
和tab.length - 1
之间,因为它用作存储值(和溢出元素)的内部数组(在本例中为tab
)的索引。因此,它不能为负数。
我假设 (hash & 0x7FFFFFFF) % tab.length
优先于 (hashcode % tab.length + tab.length) % tab.length
使用,因为它更快,不会过度增加冲突的机会,但您必须找到设计文档或与原始开发人员交谈才能确定。
【讨论】:
【参考方案2】:...但是为什么我们不能...
您在问为什么选择特定的实现。没有人能告诉你这一点,除非是代码的原始作者,如果他或她记得的话。
总是有多种方法可以在代码中实现一个想法。编写代码的人必须选择其中之一。事后问为什么没有选择另一个特定的实现是没有多大意义的。
【讨论】:
我没有检查过,但我猜是这样。为什么您对它的实现方式不满意? @Jesper:恕我直言,这确实很有意义,所以我们可以从这个决定中学习。当然,很多时候没有人可以肯定地说,但是可以找到并评估论点。这使问题成为一种讨论,在 SO 上不受欢迎,但它非常有用。【参考方案3】:如果你的容量是 2 的幂,
private static final int CAPACITY = 64;
private static final int HASH_MASK = CAPACITY - 1;
final int index = obj.hashCode() & HASH_MASK;
基本上,屏蔽掉除您感兴趣的低位之外的所有位。假设低 N 位的分布与整个哈希码一样均匀。
【讨论】:
【参考方案4】:Java 没有原生的无符号类型。如果hashCode
有负值,那么我们将不得不在使用hashCode
作为数组索引的任何地方应用这种屏蔽技巧。
【讨论】:
【参考方案5】:我们不能将有符号的 int 视为无符号的表面上是有一个很好的理由:最初的 Java 开发人员认为无符号支持是不必要的复杂化,因为无符号算术可以是 confusing。从那以后,这对 Java 来说并不是一个足够大的问题来解决。
作为verdesmerald mentioned,由于没有明确的记录说明为什么选择(hash & 0x7FFFFFFF) % tab.length
而不是出于您巧妙修改的效果,尽管我们可以找到该决定的理由,但最终我们只能推测为什么会这样做.
语义的最后一点,这可能并不那么重要:Hashtable 没有使用负哈希码,而是哈希码被“转换”为索引的非负形式。
【讨论】:
【参考方案6】:没有人能告诉你为什么原作者选择了那个实现,除了他自己(也许还有他的同事)。无论如何这并不重要,因为它工作正常。
关于您提议的实现:它可能没有按照您认为应该做的那样做。您应该刷新 java 中 % 运算符的实际作用:For example here。将整数溢出添加到混合中,您建议的表达式可能会导致负值...
【讨论】:
以上是关于哈希表哈希避免负哈希码的主要内容,如果未能解决你的问题,请参考以下文章