题
我正在考虑使用 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个字节和代表它们作为长。因此,这意味着,在双重计算的微小变化可能意味着很大,你将有关键的失误。
如果可以解决的点之后的给定数量的点 - 乘以10 ^(的点后位数),并转换为INT(例如 - 对于2位数字乘以100)。
这将是更安全。
其他提示
我想你是对的。虽然双打的哈希值是整数,双可能会搞糟的哈希值。这就是为什么,因为乔希布洛赫提到有效的Java,当你使用双作为输入到哈希函数,你应该使用的 doubleToLongBits的()。类似地,使用用于floatToIntBits浮
在特别是使用双作为您的哈希,以下乔希布洛赫的食谱,你会怎么做:
public int hashCode() {
int result = 17;
long temp = Double.doubleToLongBits(the_double_field);
result = 37 * result + ((int) (temp ^ (temp >>> 32)));
return result;
}
这是从有效的Java项目8,“总是当你重写equals重载hashCode”。它可以在本PDF从书中的章节中找到。
希望这有助于。
这取决于您将如何使用它。
如果您对仅能够根据以下值找到值感到满意 完全相同的位模式 (或者 潜在地 一个等效的,例如 +/- 0 和各种 NaN)那么可能没问题。
特别是,所有 NaN 最终都会被视为相等,但 +0 和 -0 将被视为不同。从文档中 Double.equals
:
请注意,在大多数情况下,对于两个 类 Double 的实例 d1 和 d2、 d1.equals(d2) 的值为真,如果 只有当
d1.doubleValue() == d2.doubleValue() 的值也是 确实如此。然而,有两个 例外情况:
- 如果 d1 和 d2 都代表 Double.NaN,则等号方法 返回 true,即使 Double.NaN==Double.NaN 的值为 假的
- 如果 d1 表示 0.0,而 d2 代表 -0.0,反之亦然。 等价测试的值为假,即使 虽然 0.0==-0.0 的值为 true。
该定义允许哈希表正确运行。
不过,您很可能对“非常接近关键的数字”感兴趣,这使得它的可行性大大降低。特别是,如果您打算执行一组计算来获取密钥一次,然后执行一组不同的计算来第二次获取密钥,那么您就会遇到问题。
问题是不哈希码但双打的精度。这将导致一些奇怪的结果。例如:
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,所以你没有找到在地图(哈希码大概也有所不同,但这不是主要问题)的条目。
如果您使用
map.get(key)
应该找到入口... []]
简短的回答:它可能不会正常工作
诚实的答案:这完全取决于
长回答:哈希码不是问题,它的浮点相等比较的性质。如Nalandial和在他的主题的评论者指出,最终对哈希表的任何匹配最终还是使用equals挑选合适的值。
所以,在这样的,你知道,等于实际上意味着等于方式产生的问题,你的双打?如果你读或计算的值,将其存储在哈希表中,再后来读或使用完全相同的计算计算值,然后将Double.equals工作。但除此之外,它是不可靠的:1.2 + 2.3不一定等于3.5,它可能等于3.4999995或什么的。 (不是一个真实的例子,我刚编的,但是这是那种这种情况发生的事。)你可以比较花车和可靠合理的双打小于或大于,而不是平等的。
的BigDecimal GET你,你想去哪里?
使用双的哈希,而不是双本身。
编辑:谢谢,乔恩,我居然不知道
我不知道这(你应该只是看看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);
将是危险的