问题 “规范化”BigDecimal的哈希码:howto?


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

对于JSON Schema的需求,特别需要:对于数值,JSON值相等由其数学值的相等性定义。我需要这种检查,因为,例如,这不是一个合法的模式(一个中的值) enum 应该是独一无二的):

{ "enum": [ 1, 1.0 ] }

但JsonNodes为 1 和 1.0 不平等因此,我编写了番石榴的实现 等价,并使用 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

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

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

编辑:等效实现的完整代码 这里


11059
2018-01-14 02:33


起源

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


答案:


转换为Double并使用Double的hashCode,但在BigDecimal compareTo顺序上使用基本相等。

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


16
2018-01-14 04:19



我用 .compareTo() 为了平等。这是一个如此简单的解决方案,我没有想到它...... - fge
不过,我很好奇,对于非常大的值,返回的是什么双值 double 由于缺乏精确度无法处理? - fge
所有大于Double.MAX_VALUE的数字都将映射到无穷大,并获得相同的哈希码。同样,非常小的数字将映射为零,并获得相同的哈希码。否则,在16个最高有效数字中匹配的不同数字对将获得相同的哈希码。 - Patricia Shanahan
非常感谢你的帮助!我从来没有这样做过。 - fge
好主意!一点点正交思考通常很有成效。 - Hot Licks