我经常看到类似以下代码:
int hashCode(){
return a^b;
}
为什么要用异或运算(XOR)?
XOR是所有位运算中具有最佳位混洗属性的。
以下真值表说明了这一点:
A B AND
0 0 0
0 1 0
1 0 0
1 1 1
A B OR
0 0 0
0 1 1
1 0 1
1 1 1
A B XOR
0 0 0
0 1 1
1 0 1
1 1 0
如你所见,对于AND和OR运算,在位混合方面表现不佳。
平均来说,OR将产生3/4的1比特位。而AND则相反,平均产生3/4的零比特位。只有XOR具有一比特位与零比特位分布均匀的特点。这使得它在哈希码生成中非常有价值。
请记住,在哈希码中,您要尽可能使用键的所有信息并获得良好的哈希值分布。如果您使用AND或OR,您将获得偏向于具有许多零或许多一的数字。
XOR 具有以下优点:
更多信息请参见这里。
XOR运算符是可逆的,即假设我有一个位串为0 0 1
,我将其与另一个位串1 1 1
进行XOR运算,输出结果为
0 xor 1 = 1
0 1 = 1
1 1 = 0
0 1 = 1
0 1 = 1
1 0 = 1
因此,这使得第二个字符串成为关键。其他位运算符不具备这种行为。
请参阅此处以获取更多信息 -> 为什么在密码学中使用异或?
还有另一种用例:对象中需要比较某些字段而不考虑它们的顺序。例如,如果您希望对成对的 (a, b) 始终等于成对的 (b, a)。
XOR 具有这样的属性:a ^ b
= b ^ a
,因此可以在这种情况下将其用作哈希函数。
示例:(完整代码 在这里)
定义:
final class Connection {
public final int A;
public final int B;
// some code omitted
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Connection that = (Connection) o;
return (A == that.A && B == that.B || A == that.B && B == that.A);
}
@Override
public int hashCode() {
return A ^ B;
}
// some code omitted
}
使用方法:
HashSet<Connection> s = new HashSet<>();
s.add(new Connection(1, 3));
s.add(new Connection(2, 3));
s.add(new Connection(3, 2));
s.add(new Connection(1, 3));
s.add(new Connection(2, 1));
s.remove(new Connection(1, 2));
for (Connection x : s) {
System.out.println(x);
}
// output:
// Connection{A=2, B=3}
// Connection{A=1, B=3}