我有一系列具有long
字段的对象,其值唯一地标识整个系统中的特定对象,非常类似于GUID。我已经覆盖Object.equals()
以使用此ID进行比较,因为我希望它能够使用该对象的副本。现在我想覆盖Object.hashCode()
,这基本上意味着将long
映射到某个int
返回值。
如果我正确理解hashCode
的目的,它主要用于哈希表,因此需要均匀分布。这意味着,只需返回id % 2^32
即可。这就是全部,还是我应该注意别的什么?
答案 0 :(得分:83)
从Java 8开始,您可以使用
Long.hashCode(guid);
对于旧版本的Java,您可以使用以下内容:
Long.valueOf(guid).hashCode();
请注意,此解决方案为堆栈创建了一个新的Object,而第一个没有(尽管Java可能会优化对象创建...)
查看文档,两种方法都只使用以下算法:
(int)(this.longValue()^(this.longValue()>>>32))
这些都是不错的解决方案,因为它们使用了Java库 - 总是更好地利用已经测试过的东西。
答案 1 :(得分:9)
如果你还没有使用Guava,这是一件小事,但Guava可以很好地do this for you:
public int hashCode() {
return Longs.hashCode(id);
}
这相当于Long.valueOf(id).hashCode()
:
return (int) (value ^ (value >>> 32));
此外,如果你有其他值或对象是哈希码的一部分,你可以写
return Objects.hashCode(longValue, somethingElse, ...);
long
会被自动装箱到Long
中,因此您可以获得正确的哈希码作为整体哈希码的一部分。
答案 2 :(得分:5)
您已正确理解hashCode
的目的。是的,需要统一分布(尽管不是实际要求)。
我建议((id >> 32) ^ id)
。
上面的表达:
答案 3 :(得分:3)
Java 8将Long.hashCode(long)添加到JDK。
以下代码可以产生更高的性能。此代码将计算减少到32位int
,而不是使用64位long
进行计算。这可以对32位和更小的体系结构产生影响。 x86机器上的32位进程可以将其优化为单个指令,只需对2个寄存器进行异或。
return (int)(value ^ (value >>> 32));
正如其他答案所述,不有一个好avalanche effect,因此可能会导致冲突。人们可以使用加密散列函数来确保高雪崩效应。但是,还有其他算法,如Murmur Hash(更多information),它们具有非常好的雪崩效应,但不会消耗太多的CPU时间。
答案 4 :(得分:2)
(l >> 32) ^ l
是一个很好的哈希码;特别是当长期分布均匀时。
由于这是公认的答案,我发布这篇文章是为了澄清一些关于何时它不是很好的哈希码的评论。
我给出的例子是像这样的Point类:
public class Point {
private final long coords; //x in high-bits, y in low
public int getX() {
return (int)(coords >> 32);
}
public int getY() {
return (int)coords;
}
public int hashCode() {
return (int)((coords >> 32) ^ (coords));
}
}
这可能看起来很人为,但偶尔你会把多个“字段”打包成长篇。
因此coords
字段表示32位x和32位y。那为什么这是一个问题呢?好吧,如果x和y中的每一个均匀分布在它们各自的32位上,则不是这样。但这在实践中不太可能。更有可能的是,X和Y受某些数字的限制。让我们说1024,因为它是2 ^ 10。这意味着最多设置每个X和Y的低10位:
00000000 00000000 000000XX XXXXXXXX 00000000 00000000 000000YY YYYYYYYY
有2 ^ 20(1024 * 1024)种可能的组合。但是hashCode正在做什么操作?
00000000 00000000 000000XX XXXXXXXX
^ 00000000 00000000 000000YY YYYYYYYY
-------------------------------------
= 00000000 00000000 000000?? ????????
最多有2 ^ 10(1024)个可能的hashCode值,因为只有低10位可以是除零之外的任何值。哈希值与实际值的比率为1024:(1024*1024)
或1:1024
。所以马上有一个1/1024概率,两个数字具有相同的哈希值。
现在让我们通过应用birthday problem的数学来计算碰撞的概率。设p(n)是n值存在至少一次碰撞的概率。我们知道p(1025+)= 1,因为只有1024个值。
p(n) = 1 - (n! * (1024 choose n))/1024^n
这可以解决以下问题:
n: p(n)
1: 0.00000
2: 0.00098
3: 0.00293
4: 0.00585
5: 0.00973
6: 0.01457
...
38: 0.50096
...
79: 0.95444
...
148: 0.99999
只有38件物品,可能会发生碰撞。有148个项目,(至少一个)碰撞的可能性为99.999%。有148个项目,每个项目有7%的机会与另一个项目发生碰撞。通过适当的散列函数,获取域的知识,这些数字可以很容易地降到0。
换句话说,了解您的域以及实际情况如何发生是制作高性能哈希的关键。图书馆职能部门尽可能地完成对您的领域一无所知的工作,并且高效率通常依赖于在实践中不会发生的数据分发。
答案 5 :(得分:1)
int result = (int)((longVal >> 32) ^ longVal);
将更好地分布,因为如果长值的高位已经改变,模数将不会返回不同的值。