在HashMap中加倍

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

任何人都可以在这里证实我的任何猜测吗?

简短的回答:不要这样做

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

实际的键是java.lang.Double对象,因为键必须是对象。 这是它的hashCode()方法:

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

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

如果你可以在点之后满足给定的点数 – 乘以10 ^(点后的位数)并转换为int(例如 – 对于2位乘以100)。

它会更安全。

我想你是对的。 虽然双打的哈希值是整数,但是双精度可能会破坏哈希值。 这就是为什么,正如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”。 它可以在本书章节的pdf中找到。

希望这可以帮助。

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

如果您只是能够根据完全相同的位模式 (或者可能是等效的位模式 ,例如+/- 0和各种NaN)找到值,那么它可能没问题。

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

请注意,在大多数情况下,对于Double,d1和d2类的两个实例,d1.equals(d2)的值为true且当且仅当

d1.doubleValue()== d2.doubleValue()的值也为true。 但是,有两个例外:

  • 如果d1和d2都表示Double.NaN,则equals方法返回true,即使Double.NaN == Double.NaN的值为false。
  • 如果d1表示+0.0而d2表示-0.0,反之亦然,则等值测试的值为false,即使+0.0 == – 0.0的值为true。

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

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

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

  double x = 371.4; double y = 61.9; double key = x + y; // expected 433.3 Map map = new HashMap(); map.put(key, "Sum of " + x + " and " + y); System.out.println(map.get(433.3)); // prints null 

计算值(键)是“433.29999999999995”,它不是与433.3的EQUALS,所以你没有在Map中找到条目(哈希码可能也不同,但这不是主要问题)。

如果你使用

 map.get(key) 

它应该找到条目… []]

简短的回答:它可能不起作用。

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

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

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

也许BigDecimal可以让你到达你想去的地方?

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

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

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

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

 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); 

会很危险的