2

我最近在 java 中计算双精度元组的哈希码时遇到了一个奇怪的情况。假设您有两个元组 (1.0,1.0) 和 (Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY)。使用Joshua Bloch 的 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);
}
4

2 回答 2

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(infinity 的哈希码)是 0x7ff00000。那种散列和那种乘数会产生那种效果......

执行摘要:这是一个巧合,但不要担心 :)

于 2009-12-24T21:12:44.143 回答
0

这可能是巧合,但是当您尝试在 Map 中使用 hashCode 来缓存元组中具有双精度的对象时,这肯定无济于事。我在创建恒温器温度设置类的地图时遇到了这个问题。然后其他测试失败了,因为我在使用 hashCode 作为键时从 Map 中获取了错误的对象。

我发现解决此问题的解决方案是创建 2 个双参数的附加字符串,并在字符串上调用 hashCode()。为了避免字符串开销,我缓存了哈希码。

私有易失散列码;
@Override public int hashCode()
{
  整数结果 = 哈希码;
  如果(结果 == 0){
     字符串值 = new StringBuilder().append(d1).append(d2).toString();
     结果 = value.hashCode();
     哈希码 = 结果;
  }
  返回结果;
}
于 2011-03-23T19:01:53.563 回答