在HashMap中加倍

时间:2009-07-02 14:38:54

标签: java hashcode

我正在考虑使用Double作为HashMap的关键,但我知道浮点数比较是不安全的,这让我思考。 Double类的equals方法也不安全吗?如果那样则意味着hashCode方法可能也是错误的。这意味着使用Double作为HashMap的关键将导致不可预测的行为。

有人可以在这里证实我的任何推测吗?

8 个答案:

答案 0 :(得分:13)

简短回答:不要这样做

答案很长:以下是计算密钥的方法:

实际密钥将是java.lang.Double对象,因为密钥必须是对象。这是hashCode()方法:

public int hashCode() {
  long bits = doubleToLongBits(value);
  return (int)(bits ^ (bits >>> 32));
}

doubleToLongBits()方法基本上占用8个字节并将它们表示为long。因此,这意味着双倍计算中的微小变化意味着很多,而且你会遇到关键的失误。

如果你可以在点之后找到一定数量的点 - 乘以10 ^(点后面的位数)并转换为int(例如 - 对于2位乘以100)。

它会更安全。

答案 1 :(得分:8)

我认为你是对的。虽然双打的哈希值是整数,但是double可能会破坏哈希值。正如Josh Bloch在Effective Java中提到的那样,当你使用double作为哈希函数的输入时,你应该使用doubleToLongBits()。同样,对浮点数使用floatToIntBits。

特别是,要使用double作为哈希,遵循Josh Bloch的配方,你会这样做:

public int hashCode() {
  int result = 17;
  long temp = Double.doubleToLongBits(the_double_field);
  result = 37 * result + ((int) (temp ^ (temp >>> 32)));
  return result;
}

这来自Effective Java的第8项,“当你覆盖equals时总是覆盖hashCode”。它可以在this pdf of the chapter from the book中找到。

希望这有帮助。

答案 2 :(得分:6)

这取决于你将如何使用它。

如果您对能够根据完全相同的位模式(或可能等效的)找到值感到满意,例如+/- 0和各种NaNs然后它可能没关系。

特别是,所有NaN最终都被视为相等,但+ 0和-0将被视为不同。来自Double.equals的文档:

  

请注意,在大多数情况下,两个   类Double,d1和d2的实例,   如果,则d1.equals(d2)的值为true   并且只有

     

d1.doubleValue()==   d2.doubleValue()也有值   真正。但是,有两个   例外:

     
      
  • 如果d1和d2都代表   Double.NaN,然后​​是equals方法   即使返回true也是如此   Double.NaN == Double.NaN具有该值   假的。
  •   
  • 如果d1代表+0.0而d2代表   代表-0.0,反之亦然   等值测试的值甚至是假的   虽然+0.0 == - 0.0的值为true。   
     

此定义允许哈希表   运作正常。

尽管你很可能对“非常接近关键的数字”感兴趣,这使得它不那么可行。特别是如果你要进行一组计算以获得一次密钥,那么第二次获得密钥的另一组计算,你就会遇到问题。

答案 3 :(得分:5)

问题不是哈希码,而是双精度。这会导致一些奇怪的结果。例如:

    double x = 371.4;
    double y = 61.9;
    double key = x + y;    // expected 433.3

    Map<Double, String> map = new HashMap<Double, String>();
    map.put(key, "Sum of " + x + " and " + y);

    System.out.println(map.get(433.3));  // prints null

计算值(键)是“433.29999999999995”,它不是与433.3的EQUALS,因此您在Map中找不到该条目(哈希码可能也不同,但这不是主要问题)。 / p>

如果您使用

map.get(key)

它应该找到条目...... []]

答案 4 :(得分:3)

简短回答:它可能无效。

诚实的回答:一切都取决于。

更长的答案:哈希码不是问题,它是浮点上相等比较的本质。正如Nalandial及其帖子中的评论者指出的那样,最终任何与哈希表的匹配仍然会使用等于选择正确的值。

所以问题是,你的双打是否以这样的方式产生,你知道等于真的意味着平等?如果您读取或计算值,将其存储在哈希表中,然后使用完全相同的计算读取或计算值,则Double.equals将起作用。但除此之外它是不可靠的:1.2 + 2.3不一定等于3.5,它可能等于3.4999995或其他什么。 (不是一个真实的例子,我只是把它做了,但这就是发生的事情。)你可以合理地可靠地比较浮点数和双打数,但不是等于。

答案 5 :(得分:2)

也许BigDecimal可以让你到达目的地?

答案 6 :(得分:0)

使用double的哈希值,而不是double本身。

编辑:谢谢,Jon,我其实并不知道。

我不确定这一点(您应该只看一下Double对象的源代码),但我认为浮点数比较的任何问题都会为您解决。

答案 7 :(得分:0)

这取决于您如何存储和访问您的地图,是的,类似的值可能会略有不同,因此不会散列到相同的值。

private static final double key1 = 1.1+1.3-1.6;
private static final double key2 = 123321;
...
map.get(key1);

会很好,但是

map.put(1.1+2.3, value);
...
map.get(5.0 - 1.6);

会很危险