Java HashMap 中的双倍

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1074781/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:12:02  来源:igfitidea点击:

Double in HashMap

javahashcode

提问by anio

I was thinking of using a Double as the key to a HashMap but I know floating point comparisons are unsafe, that got me thinking. Is the equals method on the Double class also unsafe? If it is then that would mean the hashCode method is probably also incorrect. This would mean that using Double as the key to a HashMap would lead to unpredictable behavior.

我想使用 Double 作为 HashMap 的键,但我知道浮点比较是不安全的,这让我开始思考。Double 类上的 equals 方法是否也不安全?如果是,那意味着 hashCode 方法可能也不正确。这意味着使用 Double 作为 HashMap 的键会导致不可预测的行为。

Can anyone confirm any of my speculation here?

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

采纳答案by David Rabinowitz

Short answer:Don't do it

简短回答:不要这样做

Long answer:Here is how the key is going to be computed:

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

The actual key will be a java.lang.Doubleobject, since keys must be objects. Here is its hashCode()method:

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

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

The doubleToLongBits()method basically takes the 8 bytes and represent them as long. So it means that small changes in the computation of double can mean a great deal and you will have key misses.

doubleToLongBits()方法基本上采用 8 个字节并将它们表示为长。所以这意味着 double 计算的微小变化可能意味着很大,你将有关键的失误。

If you can settle for a given number of points after the dot - multiply by 10^(number of digits after the dot) and convert to int (for example - for 2 digits multiply by 100).

如果您可以在点后确定给定的点数 - 乘以 10^(点后的位数)并转换为 int(例如 - 2 位数乘以 100)。

It will be much safer.

会安全很多。

回答by Alex Beardsley

The hash of the double is used, not the double itself.

使用双精度的哈希值,而不是双精度本身。

Edit:Thanks, Jon, I actually didn't know that.

编辑:谢谢,乔恩,我实际上不知道。

I'm not sure about this (you should just look at the source code of the Double object) but I would think any issues with floating point comparisons would be taken care of for you.

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

回答by Jon Skeet

It depends on how you would be using it.

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

If you're happy with only being able to find the value based on the exact same bit pattern(or potentiallyan equivalent one, such as +/- 0 and various NaNs) then it might be okay.

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

In particular, all NaNs would end up being considered equal, but +0 and -0 would be considered different. From the docs for Double.equals:

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

Note that in most cases, for two instances of class Double, d1 and d2, the value of d1.equals(d2) is true if and only if

d1.doubleValue() == d2.doubleValue() also has the value true. However, there are two exceptions:

  • If d1 and d2 both represent Double.NaN, then the equals method returns true, even though Double.NaN==Double.NaN has the value false.
  • If d1 represents +0.0 while d2 represents -0.0, or vice versa, the equal test has the value false, even though +0.0==-0.0 has the value true.

This definition allows hash tables to operate properly.

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

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。

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

Most likely you're interested in "numbers very close to the key" though, which makes it a lot less viable. In particular if you're going to do one set of calculations to get the key once, then a different set of calculations to get the key the second time, you'll have problems.

不过,您很可能对“非常接近键的数字”感兴趣,这使得它不太可行。特别是如果您要进行一组计算以获取一次密钥,然后进行另一组计算以第二次获取密钥,则会遇到问题。

回答by Tom

I think you are right. Although the hash of the doubles are ints, the double could mess up the hash. That is why, as Josh Bloch mentions in Effective Java, when you use a double as an input to a hash function, you should use doubleToLongBits(). Similarly, use floatToIntBits for floats.

我想你是对的。虽然双打的散列是整数,但双打可能会弄乱散列。这就是为什么,正如 Josh Bloch 在 Effective Java 中提到的,当您使用 double 作为哈希函数的输入时,您应该使用doubleToLongBits()。同样,对浮点数使用 floatToIntBits。

In particular, to use a double as your hash, following Josh Bloch's recipe, you would do:

特别是,要按照 Josh Bloch 的配方使用 double 作为您的哈希,您可以这样做:

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

This is from Item 8 of Effective Java, "Always override hashCode when you override equals". It can be found in this pdf of the chapter from the book.

这是来自 Effective Java 的第 8 条,“当您覆盖 equals 时,始终覆盖 hashCode”。它可以在这本书的章节的 pdf 中找到。

Hope this helps.

希望这可以帮助。

回答by David Waters

It depends on how you store and access you map, yes similar values could end up being slightly different and therefore not hash to the same value.

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

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

would be all good, however

一切都很好,但是

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

would be dangerous

会很危险

回答by user85421

The problem is not the hash code but the precision of the doubles. This will cause some strange results. Example:

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

    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

The calculated value (key) is "433.29999999999995" which is not EQUALS to 433.3 and so you don't find the entry in the Map (the hash code probably is also different, but that is not the main problem).

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

If you use

如果你使用

map.get(key)

it should find the entry... []]

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

回答by Brian T. Grant

Maybe BigDecimalget you where you want to go?

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

回答by Jay

Short answer: It probably won't work.

简短的回答:它可能行不通。

Honest answer: It all depends.

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

Longer answer: The hash code isn't the issue, it's the nature of equal comparisons on floating point. As Nalandial and the commenters on his post point out, ultimately any match against a hash table still ends up using equals to pick the right value.

更长的答案:哈希码不是问题,而是浮点数相等比较的性质。正如 Nalandial 和他帖子中的评论者指出的那样,最终任何与哈希表的匹配最终仍会使用 equals 来选择正确的值。

So the question is, are your doubles generated in such a way that you know that equals really means equals? If you read or compute a value, store it in the hash table, and then later read or compute the value using exactly the same computation, then Double.equals will work. But otherwise it's unreliable: 1.2 + 2.3 does not necessarily equal 3.5, it might equal 3.4999995 or whatever. (Not a real example, I just made that up, but that's the sort of thing that happens.) You can compare floats and doubles reasonably reliably for less or greater, but not for equals.

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