位运算符 >>> 的意思是将二进制数向右移动那么多位,同时在最高有效位填充0。但是,为什么以下操作会产生相同的数字:5>>>32得到5,-5>>>32得到-5。因为如果上述描述正确,那么这两个操作的最终结果都应该为0。
继续上面的话题,根据Effective Java书籍,在计算哈希码时(如果字段是长整型),我们应该使用(int)(f ^ (f >>> 32))。为什么我们要这样做,有什么解释吗?
位运算符 >>> 的意思是将二进制数向右移动那么多位,同时在最高有效位填充0。但是,为什么以下操作会产生相同的数字:5>>>32得到5,-5>>>32得到-5。因为如果上述描述正确,那么这两个操作的最终结果都应该为0。
继续上面的话题,根据Effective Java书籍,在计算哈希码时(如果字段是长整型),我们应该使用(int)(f ^ (f >>> 32))。为什么我们要这样做,有什么解释吗?
5
可以表示为0101
,如果你将它向右移动1位,即5>>>1
,结果将会是0010=2
如果左操作数的提升类型为int,则只使用右操作数的最低5位作为移位距离。就好像右操作数被位逻辑AND运算符&(§15.22.1)与掩码值0x1f进行了处理一样。实际使用的移位距离因此始终在0到31之间,包括0和31。
当您使用<<或>>运算符移位整数时,移位距离大于或等于32时,您需要对32取模(换句话说,您需要屏蔽移位距离除低阶5位以外的所有位)。这可能非常反直觉。例如,对于每个整数i,(i>>32)==i。您可能期望它将整个数字向右移动,对于正数输入返回0,对于负数输入返回-1,但它不会;它只是返回i,因为(i<<(32&0x1f))==(i<<0)==i。
我知道这个问题早已得到解答,但是我尝试了一个例子来获得更多的澄清,我想这也会对其他人有所帮助。
long x = 3231147483648l;
System.out.println(Long.toBinaryString(x));
System.out.println(Long.toBinaryString(x >>> 32));
System.out.println(Long.toBinaryString(x ^ (x >>> 32)));
System.out.println(Long.toBinaryString((int) x ^ (x >>> 32)));
这将打印出-
101111000001001111011001011110001000000000
1011110000
101111000001001111011001011110000011110000
1001111011001011110000011110000
如@avrilfanomar所提到的,这个操作使用异或运算符将长整型的前32位与其他32位进行异或操作,并使用无符号右移运算符来帮助我们执行此操作。由于我们想在计算哈希码时使用这个长整型字段,直接将 long
强制转换为 int
将意味着只有高32位不同的long
字段将对哈希码贡献相同的值。这可能意味着只有在这个字段不同的两个对象将具有相同的哈希码,并且它们将存储在同一个桶中(例如使用列表解决碰撞),这会影响基于哈希的集合的性能。因此,进行此操作。
您第一个问题的答案在这里:为什么1>>32等于1?
简而言之,第二个问题的答案是这种方式使用了整个长整型值(而不是其中的一部分),请注意这可能是最快的方法。