我最近在java中计算双元组元组的哈希码时遇到了一个奇怪的情况。假设您有两个元组(1.0,1.0)和(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY)。使用Joshua Bloch's Effective Java(第7项)中所述的习语,这两个元组不会被认为是相等的(想象一下这些元组是对象)。但是,使用第8项中所述的公式计算每个元组的hashCode()
评估为相同的值。
所以我的问题是:在编写公式时我错过了这个公式有什么奇怪的,或者只是一个奇怪的哈希码碰撞案例?
这是我用来说明情况的简短比较方法(我把它写成JUnit4测试,但它应该很容易转换为main
方法)。
@Test
public void testDoubleHashCodeAndInfinity(){
double a = 1.0;
double b = 1.0;
double c = Double.POSITIVE_INFINITY;
double d = Double.POSITIVE_INFINITY;
int prime = 31;
int result1 = 17;
int result2 = 17;
long temp1 = Double.doubleToLongBits(a);
long temp2 = Double.doubleToLongBits(c);
//this assertion passes successfully
assertTrue("Double.doubleToLongBits(Double.POSITIVE_INFINITY" +
"==Double.doubleToLongBits(1.0)",temp1!=temp2);
result1 = prime*result1 + (int)(temp1^(temp1>>>32));
result2 = prime*result2 + (int)(temp2^(temp2>>>32));
//this assertion passes successfully
assertTrue("Double.POSITIVE_INFINITY.hashCode()" +
"==(1.0).hashCode()",result1!=result2);
temp1 = Double.doubleToLongBits(b);
temp2 = Double.doubleToLongBits(d);
//this assertion should pass successfully
assertTrue("Double.doubleToLongBits(Double.POSITIVE_INFINITY" +
"==Double.doubleToLongBits(1.0)",temp1!=temp2);
result1 = prime*result1+(int)(temp1^(temp1>>>32));
result2 = prime*result2+(int)(temp2^(temp2>>>32));
//this assertion fails!
assertTrue("(1.0,1.0).hashCode()==" +
"(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY).hashCode()",
result1!=result2);
}
答案 0 :(得分:5)
这只是一个巧合。然而,这是一个有趣的。试试这个:
Double d1 = 1.0;
Double d2 = Double.POSITIVE_INFINITY;
int hash1 = d1.hashCode();
int hash2 = d2.hashCode();
// These both print -1092616192
// This was me using the wrong hash combinator *and*
// the wrong tuples... but it's interesting
System.out.println(hash1 * 17 + hash2);
System.out.println(hash2 * 17 + hash1);
// These both print -33554432
System.out.println(hash1 * 31 + hash1);
System.out.println(hash2 * 31 + hash2);
基本上,哈希的位模式决定了这一点。 hash1(1.0的哈希码)是0x3ff00000,hash2(无穷大的哈希码)是0x7ff00000。那种哈希和那种乘法产生了那种效果......
执行摘要:这是巧合,但不要担心:)
答案 1 :(得分:0)
这可能是巧合,但是当您尝试在Map中使用hashCode来缓存元组中具有双精度的对象时,这肯定无济于事。我在创建Thermostat临时设置类的地图时遇到了这个问题。然后其他测试失败了,因为当使用hashCode作为密钥时,我从Map中获取了错误的对象。
我发现修复此问题的解决方案是创建一个2个双参数的附加字符串,并在String上调用hashCode()。为了避免字符串开销,我缓存了哈希码。
private volatile hashCode; @Override public int hashCode() { int result = hashCode; if (result == 0) { String value = new StringBuilder().append(d1).append(d2).toString(); result = value.hashCode(); hashCode = result; } return result; }