java - 为什么 BigDecimal 自然排序与 equals 不一致?

标签 java bigdecimal

来自 the Javadoc for BigDecimal :

Note: care should be exercised if BigDecimal objects are used as keys in a SortedMap or elements in a SortedSet since BigDecimal's natural ordering is inconsistent with equals.

例如,如果您创建一个 HashSet 并将 new BigDecimal("1.0")new BigDecimal("1.00") 添加到它,该集合将包含两个元素(因为值具有不同的比例,因此根据 equalshashCode 不相等),但如果你做同样的事情TreeSet,该集合将只包含一个元素,因为当您使用 compareTo 时,值比较相等。

这种不一致背后有什么具体原因吗?

最佳答案

来自 OpenJDK implementation BigDecimal:

/**
     * Compares this {@code BigDecimal} with the specified
     * {@code Object} for equality.  Unlike {@link
     * #compareTo(BigDecimal) compareTo}, this method considers two
     * {@code BigDecimal} objects equal only if they are equal in
     * value and scale (thus 2.0 is not equal to 2.00 when compared by
     * this method).
     *
     * @param  x {@code Object} to which this {@code BigDecimal} is 
     *         to be compared.
     * @return {@code true} if and only if the specified {@code Object} is a
     *         {@code BigDecimal} whose value and scale are equal to this 
     *         {@code BigDecimal}'s.
     * @see    #compareTo(java.math.BigDecimal)
     * @see    #hashCode
     */
    @Override
    public boolean equals(Object x) {
        if (!(x instanceof BigDecimal))
            return false;
        BigDecimal xDec = (BigDecimal) x;
        if (x == this)
            return true;
    if (scale != xDec.scale)
        return false;
        long s = this.intCompact;
        long xs = xDec.intCompact;
        if (s != INFLATED) {
            if (xs == INFLATED)
                xs = compactValFor(xDec.intVal);
            return xs == s;
        } else if (xs != INFLATED)
            return xs == compactValFor(this.intVal);

        return this.inflate().equals(xDec.inflate());
    }

更多的实现:

 * <p>Since the same numerical value can have different
 * representations (with different scales), the rules of arithmetic
 * and rounding must specify both the numerical result and the scale
 * used in the result's representation.

这就是为什么 equals 的实现会考虑 scale 的原因。以字符串为参数的构造函数实现如下:

    public BigDecimal(String val) {
        this(val.toCharArray(), 0, val.length());
    }

第三个参数将用于 scale(在另一个构造函数中),这就是字符串 1.01.00 将创建不同的原因BigDecimals(具有不同的比例)。

来自 Effective Java 约书亚·布洛赫:

The final paragraph of the compareTo contract, which is a strong suggestion rather than a true provision, simply states that the equality test imposed by the compareTo method should generally return the same results as the equals method. If this provision is obeyed, the ordering imposed by the compareTo method is said to be consistent with equals. If it’s violated, the ordering is said to be inconsistent with equals. A class whose compareTo method imposes an order that is inconsistent with equals will still work, but sorted collections containing elements of the class may not obey the general contract of the appropriate collection interfaces (Collection, Set, or Map). This is because the general contracts for these interfaces are defined in terms of the equals method, but sorted collections use the equality test imposed by compareTo in place of equals. It is not a catastrophe if this happens, but it’s something to be aware of.

关于java - 为什么 BigDecimal 自然排序与 equals 不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19818537/

相关文章:

java - 检索存储在数据库中的日期的时区

java - 如何在不四舍五入的情况下截断 BigDecimal

java - 幻方最终值始终为真

java - PreparedStatement 池与连接池

java - 如何使用自定义列表适配器检查 ListView 是否为空

Java BigDecimal 可以用逗号代替点吗?

java - 抵押贷款计算返回负值

java - 使 BigDecimal 截断而不是舍入

java - 大十进制除法

java - Android自定义ViewGroup绘制圆行