我有两个相关的问题:
位运算符 >>> 意味着我们将二进制数移动了许多位,同时在最高有效位中填充 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,则只有右侧操作数的五个最低位用作移位距离。就好像右手操作数经过位逻辑与运算符 & (§15.22.1) 与掩码值 0x1f。因此,实际使用的移位距离始终在 0 到 31 的范围内,包括 0 到 31。
当您使用 << 或 >> 运算符对整数进行移位且移位距离大于或等于 32 时,您将移位距离 mod 32(换句话说,您屏蔽除移位的低 5 位之外的所有位)距离)。这可能非常违反直觉。例如 (i> >> 32) == i,对于每个整数 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 所提到的,这个 XOR 的前 32 位 long 与其他 32 位和无符号右移运算符帮助我们做到这一点。由于我们想在计算哈希码时使用这个长字段,因此直接将其转换long
为int
意味着long
仅在高 32 位不同的字段将为哈希码贡献相同的值。这可能意味着仅在该字段中不同的两个对象将具有相同的哈希码,并将存储在同一个存储桶中(例如解决冲突的列表),这会影响基于哈希的集合的性能。因此,这个操作。
您的第一个问题的答案是为什么 1>>32 == 1?
简而言之,第二个问题的答案是以这种方式使用整个 long 值(而不是它的一部分),并注意这可能是最快的方法。