Java 哈希表哈希避免负哈希代码

Java 哈希表哈希避免负哈希代码,java,hash,hashtable,hashcode,Java,Hash,Hashtable,Hashcode,我想知道为什么要避免使用负哈希代码 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 (h

我想知道为什么要避免使用负哈希代码

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

该值必须介于
0
tab.length-1
之间,因为它被用作存储值(和溢出元素)的内部数组(
tab
)的索引。因此,它不能是消极的

我假设
(hash&0x7FFFFFFF)%tab.length
优先于
(hashcode%tab.length+tab.length)%tab.length使用,因为它速度更快,不会过度增加冲突的机会,但您必须找到设计文档或与原始开发人员交谈才能确定

。。。但是为什么我们不能

您会问为什么选择了特定的实现。没有人能告诉你,除非代码的原始作者,如果他或她记得的话


在代码中实现一个想法总是有多种方法的。编写代码的人必须选择其中一个。事后再问为什么没有选择另一个特定的实现没有多大意义。

Java没有本机未签名类型。如果
hashCode
的值为负值,那么我们将不得不在使用
hashCode
作为数组索引的任何地方应用这种掩蔽技巧。

除了他自己(可能还有他的同事)之外,没有人能告诉您原始作者选择该实现的原因。不管怎样,这都不重要,因为它工作得很好


关于您提议的实现:它可能没有做您认为它应该做的事情。您应该刷新java中%运算符的实际功能:。将整数溢出添加到混合中,您提出的表达式可能会导致负值…

我们不能将有符号的int视为无符号的,表面上有一个很好的理由:最初的Java开发人员认为无符号支持是一个不必要的复杂问题,因为无符号算术可能是这样。从那以后,这对Java来说还不是一个足够大的问题

同样,由于没有明确的记录表明为什么选择了
(hash&0x7FFFFFFF)%tab.length
而不是你巧妙的修改,尽管我们可以找到做出决定的理由,但最终我们只能猜测为什么会这样做


语义学的最后一点可能并不那么重要:与其说哈希表没有使用负哈希代码,倒不如说哈希代码正在被“翻译”成索引的非负形式。

如果您将容量保持为2的幂

private static final int CAPACITY = 64;
private static final int HASH_MASK = CAPACITY - 1;

final int index = obj.hashCode() & HASH_MASK;

基本上,除了你感兴趣的较低的部分外,掩盖掉所有的部分。假设较低的N位的分布与整个散列码一样均匀。

我没有检查,但我想是这样。为什么你对它的实现方式不满意?@Jesper:IMHO它确实很有意义,所以我们可以从这个决定中学习。当然,通常没有人能够确定,但可以找到并评估论点。这使得这个问题成为一种讨论,在这里不受欢迎,但它非常有用。我认为这个方法简单有效。也许这就是为什么它被使用的原因
(hash&0x7FFFFFFF)
缩小到正,
%tab.长度
缩小到tab大小。简单、干净、容易。你指的是哪种方法?最初的实现?是的。已经实现的.Integer除法和模运算是迄今为止最慢的运算(在当代Intel/AMD CPU上可能有40个周期),而
&
属于最便宜的运算(1个周期,可以并行执行)。因此,您的解决方案将需要大约两倍于原始解决方案的时间。