Java中Floats的哈希码

时间:2016-04-25 18:16:32

标签: java floating-point hashcode

我有一个包含两个浮点变量和 hashCode 方法的类(在当前代码片段中没有等号):

public class TestPoint2D {
    private float x;
    private float z;

    public TestPoint2D(float x, float z) {
        this.x = x;
        this.z = z;
    }

    @Override
    public int hashCode() {
        int result = (x != +0.0f ? Float.floatToIntBits(x) : 0);
        result = 31 * result + (z != +0.0f ? Float.floatToIntBits(z) : 0);
        return result;
    }
}

以下测试

@Test
public void tempTest() {
    TestPoint2D p1 = new TestPoint2D(3, -1);
    TestPoint2D p2 = new TestPoint2D(-3, 1);

    System.out.println(p1.hashCode());
    System.out.println(p2.hashCode());
}

返回相同的值:

  

-2025848832

在这种情况下,我不能在HashSet / HashMap中使用我的TestPoint2D

在这种情况下,有人可以建议如何实施 hashCode 或与此相关的解决方法吗?

P.S。 再增加一个测试:

@Test
public void hashCodeTest() {
    for (float a = 5; a < 100000; a += 1.5f) {
        float b = a + 1000 / a; // negative value depends on a
        TestPoint3D p1 = new TestPoint3D(a, -b);
        TestPoint3D p2 = new TestPoint3D(-a, b);
        Assert.assertEquals(p1.hashCode(), p2.hashCode());
    }
}

通过证明

TestPoint2D(a, -b).hashCode() == TestPoint2D(-a, b).hashCode()

4 个答案:

答案 0 :(得分:3)

我会使用Objects.hash()

public int hashCode() {
   return Objects.hash(x, z);
}

来自Javadoc:

  

public static int hash(Object... values)

     

为输入值序列生成哈希码。生成哈希代码就好像所有输入值都放在一个数组中一样,并且通过调用Arrays.hashCode(Object [])对该数组进行哈希处理。   此方法对于在包含多个字段的对象上实现Object.hashCode()非常有用。例如,如果一个对象有三个字段x,y和z,则可以写:

答案 1 :(得分:2)

这些自动生成的哈希码函数不是很好。

问题是小整数导致非常稀疏&#34;和类似的bitcodes。

要了解问题,请查看实际计算。

System.out.format("%x\n", Float.floatToIntBits(1));
System.out.format("%x\n", Float.floatToIntBits(-1));
System.out.format("%x\n", Float.floatToIntBits(3));
System.out.format("%x\n", Float.floatToIntBits(-3));

给出:

3f800000
bf800000
40400000
c0400000

如您所见,-是IEEE浮动中最重要的部分。使用31的乘法实际上不会改变它们:

b0800000
30800000
c7c00000
47c00000

问题是最后的所有0。它们通过整数乘法与任何素数保存(因为它们是基数为2 0,而不是基数为10!)。

恕我直言,最好的策略是采用比特移位,例如:

final int h1 = Float.floatToIntBits(x);
final int h2 = Float.floatToIntBits(z);
return h1 ^ ((h2 >>> 16) | (h2 << 16));

但您可能希望查看Which hashing algorithm is best for uniqueness and speed? test 以了解整数 - 浮动的特定情况。

答案 2 :(得分:1)

根据java规范,2个对象可以具有相同的hashCode,这并不意味着它们是相等的......

概率很小,但exist ......

另一方面,

总是优先覆盖equals和hashcode ......

答案 3 :(得分:0)

正如我理解这个问题,你期望你的密钥中有很多对称的点,所以你需要一个不会给它们提供相同代码的hashCode方法。

我做了一些测试,故意对x的符号赋予额外的意义,往往会将对称点相互远离。请参阅此测试程序:

public class Test {
  private float x;
  private float y;

  public static void main(String[] args) {
    int collisions = 0;
    for (int ix = 0; ix < 100; ix++) {
      for (int iz = 0; iz < 100; iz++) {
        Test t1 = new Test(ix, -iz);
        Test t2 = new Test(-ix, iz);
        if (t1.hashCode() == t2.hashCode()) {
          collisions++;
        }
      }
    }
    System.out.println(collisions);

  }

  public Test(float x, float y) {
    super();
    this.x = x;
    this.y = y;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = (x >= 0) ? 1 : -1;
    result = prime * result + Float.floatToIntBits(x);
    result = prime * result + Float.floatToIntBits(y);
    return result;
  }
  // Equals omitted for compactness
}

没有result = (x >= 0) ? 1 : -1;行,它是Eclipse生成的hashCode(),计算9802对称点碰撞。使用该行,它会计算一个对称的点碰撞。