我正在考虑使用Double作为HashMap的关键,但我知道浮点数比较是不安全的,这让我思考。 Double类的equals方法也不安全吗?如果那样则意味着hashCode方法可能也是错误的。这意味着使用Double作为HashMap的关键将导致不可预测的行为。
有人可以在这里证实我的任何推测吗?
答案 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);
会很危险