【问题标题】:"Normalizing" BigDecimal's hash code: howto?“规范化” BigDecimal 哈希码:如何?
【发布时间】:2013-01-14 02:33:22
【问题描述】:

我有一个用 Java 编写的 JSON Schema 实现,它依赖于 Jackson(版本 2.1.x)。出于准确性原因,我告诉 Jackson 使用 BigDecimal 表示浮点数。

对于JSON Schema的需求,有一个特殊的需求:JSON值相等,对于数值,是通过它们的数学值相等来定义的。我需要这种检查,例如,这不是一个合法的模式(enum 中的值应该是唯一的):

{ "enum": [ 1, 1.0 ] }

但是 11.0 的 JsonNodes 不相等。因此,我编写了 Guava 的Equivalence 的实现,并在适当的地方使用Set<Equivalence.Wrapper<JsonNode>>。而且这个实现应该适用于所有类型的节点,而不仅仅是数字节点。

这个实现中最困难的部分是doHash() 用于数字节点:/我需要相同的哈希码用于等效的数学值,无论它们是整数还是浮点数。

目前我能想到的最好的是:

@Override
protected int doHash(final JsonNode t)
{
    /*
     * If this is a numeric node, we want a unique hashcode for all possible
     * number nodes.
     */
    if (t.isNumber()) {
        final BigDecimal decimal = t.decimalValue();
        try {
            return decimal.toBigIntegerExact().hashCode();
        } catch (ArithmeticException ignored) {
            return decimal.stripTrailingZeros().hashCode();
        }
    }

    // etc etc -- the rest works fine

这是目前我能想到的最好的。

有没有更好的方法来计算这样的哈希码?

编辑:等价实现的完整代码here

【问题讨论】:

  • @zsxwing: doEquivalent 已经被覆盖——见编辑,我添加了一个完整实现的链接
  • 不清楚 - 是否存在代码未针对相等值返回相等哈希码的问题,或者您是否(错误地)试图确保每个不同值的唯一哈希码?
  • 您希望“1”、“1.0”、“1.00”返回相同的哈希码吗?也许您可以使用不使用 hashCode 的 TreeSet?
  • 记住哈希码的关键规则:如果两个对象比较相等,则它们必须具有相同的哈希码。
  • @HotLicks 我知道...问题是我需要两个 数学等效 BigDecimals 相同的哈希码,当然我不能依赖.equals() - - 我认为这个问题很清楚

标签: java guava hashcode bigdecimal


【解决方案1】:

转换为 Double 并使用 Double 的 hashCode,但基于 BigDecimal compareTo 顺序的相等性。

两个数值相等的 BigDecimal 将映射到同一个 Double,并获得相同的 hashCode。由于双舍入,一些略有不同的 BigDecimal 值将获得相同的哈希码,但大多数不同的值将获得不同的哈希码,这就是您所需要的。

【讨论】:

  • 我确实使用.compareTo() 来表示平等。这是一个如此简单的解决方案,我没有考虑过......
  • 不过,我很好奇,对于非常大的值返回哪些双精度值,double 由于缺乏精度而无法处理?
  • 所有大于 Double.MAX_VALUE 的数字将被映射到无穷大,并获得相同的哈希码。同样,非常小的数字将映射为零,并获得相同的哈希码。否则,在大约 16 个最高有效数字中匹配的不同数字对将获得相同的哈希码。
  • 非常感谢您的帮助!我从来没有想过这个。
  • Scala 有一个围绕 java BigDecimal 的包装器,它提供了您可能感兴趣的更好的 hashCode(和 equals)。在这里查看 computeHashCode:github.com/scala/scala/blob/2.11.x/src/library/scala/math/…
【解决方案2】:

使用BigDecimal::stripTrailingZeros()方法,它为两个不同的BigDecimal对象返回相同的BigDecimal对象,它们代表相同的数学数字,即它们具有不同数量的无意义数字,例如3.50(比例是二)和3.5(比例是一):

return decimal.stripTrailingZeros().hashCode();

【讨论】:

  • 由于@patricia-shanahan 指出的原因,这应该是公认的答案
  • 您忘记了工程符号。 stripTrailingZeros()BigDecimal("1E+2") != BigDecimal("100") 没有帮助。
猜你喜欢
  • 1970-01-01
  • 2021-10-11
  • 2022-11-22
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
  • 2012-09-16
  • 2019-05-20
  • 2015-09-16
相关资源
最近更新 更多