在Effective Java中的hashCode()实现中使用位移操作

22

我想知道有没有人能够详细解释以下哈希码实现中的代码:

(int)(l ^ (l >>> 32));

这段代码在以下哈希码实现中起到了什么作用(由Eclipse生成,但与Effective Java相同):

private int i;
private char c; 
private boolean b;
private short s;
private long l;
private double d;
private float f;

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + i;
    result = prime * result + s;
    result = prime * result + (b ? 1231 : 1237);
    result = prime * result + c;
    long t = Double.doubleToLongBits(d);
    result = prime * result + (int) (t ^ (t >>> 32));
    result = prime * result + Float.floatToIntBits(f);
    result = prime * result + (int) (l ^ (l >>> 32));
    return result;
}

谢谢!

3个回答

33

基本上它会对一个长整型的前32位与后32位进行异或运算。以下是详细说明:

// Unsigned shift by 32 bits, so top 32 bits of topBits will be 0,
// bottom 32 bits of topBits will be the top 32 bits of l
long topBits = l >>> 32;

// XOR topBits with l; the top 32 bits will effectively be left
// alone, but that doesn't matter because of the next step. The
// bottom 32 bits will be the XOR of the top and bottom 32 bits of l
long xor = l ^ topBits;

// Convert the long to an int - this basically ditches the top 32 bits
int hash = (int) xor;

回答您的评论:您有一个长整型值,必须将其转换为 int 才能成为哈希的一部分(结果必须仅为 32 位)。您要如何做到这一点?您可以选择只取底部的 32 位 - 但这意味着仅更改顶部 32 位将被忽略,这不会生成很好的哈希。这种方式,单个输入位的更改始终导致哈希的单个位的更改。诚然,您仍然可以轻松地发生碰撞 - 如更改第 7 位和第 39 位或任何其他相隔 32 个位置的一对位 - 但考虑到您从 264 可能的值到 232,这是必然的。

这样做的原因是什么?不这样做也能得到同样有效的哈希码吗? - Mark Pope
@Scobal:我已经扩展我的回答以进一步解释。 - Jon Skeet
这里常数31(素数int)的意义是什么?还是我们可以取任何一个素数? - Diffy
@Diffy:这是一个棘手的问题,在另一个与哈希相关的问题中讨论过,但我现在很难找到。我认为在这种哈希中选择这种类型的常量部分是科学,部分是黑魔法... - Jon Skeet

9
它需要一个64位数字,将其分成两半,并对这两个半部分进行异或运算(本质上是这样)。

4

这段代码需要一个(64位)long类型的变量 l,将其分为两个32位的半部分并进行异或运算,将结果的低32位存入64位结果的底部32位,然后使用(int)强制类型转换仅返回底部32位。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接