我正在考虑使用 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);

将是危险的

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top