为什么BigDecimal自然排序与equals不一致? [英] Why is BigDecimal natural ordering inconsistent with equals?

查看:105
本文介绍了为什么BigDecimal自然排序与equals不一致?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Javadoc获取 BigDecimal


注意:如果 BigDecimal 对象用作 SortedMap 中的键或 SortedSet 中的元素,因为 BigDecimal 's 自然排序 与equals 不一致。


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



这种不一致背后是否有任何具体原因?


解决方案

来自 OpenJDK实现

  / ** 
*将此{@code BigDecimal}与指定的
* {@code Object}进行比较以获得相等性。与{@link
* #compareTo(BigDecimal)compareTo}不同,此方法只考虑两个
* {@code BigDecimal}对象,只要它们在
*值和比例(即2.0)中相等当用
*这种方法比较时,它不等于2.00。
*
* @param x {@code Object},此{@code BigDecimal}是
*,需要进行比较。
* @return {@code true}当且仅当指定的{@code Object}是
* {@code BigDecimal}时,其值和比例等于此
* {@代码BigDecimal}的。
* @see #compareTo(java.math.BigDecimal)
* @see #hashCode
* /
@Override
public boolean equals(Object x){
if(!(x instanceof BigDecimal))
返回false;
BigDecimal xDec =(BigDecimal)x;
if(x == this)
返回true;
if(scale!= xDec.scale)
返回false;
long s = this.intCompact;
long xs = xDec.intCompact;
if(s!= INFLATED){
if(xs == INFLATED)
xs = compactValFor(xDec.intVal);
返回xs == s;
} else if(xs!= INFLATED)
返回xs == compactValFor(this.intVal);

返回this.inflate()。equals(xDec.inflate());
}

执行中的更多信息:

  *< p>由于相同的数值可以有不同的
*表示(具有不同的比例),算术规则
*和舍入必须指定数值结果和结果表示中使用的比例
*。

这就是为什么的实现等于需要考虑比例。将字符串作为参数的构造函数实现如下:

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

其中第三个参数将用于比例(在另一个构造函数中)这就是为什么字符串 1.0 1.00 将创建不同的BigDecimals (具有不同的比例)。



来自 有效的Java 作者:Joshua Bloch:


compareTo合约的最后一段,这是一个强大的
建议而不是真正的规定,只是声明compareTo方法强加的
等式测试通常应该返回
与equals方法相同的结果。如果遵守这一规定,则
,compareTo方法所施加的排序据说与b等于
。如果它被违反,则说明订单与b等于
不一致。 compareTo方法强加订单
与equals不一致的类仍然有效,但是包含该类元素的已排序集合
可能无法遵守
的常规合约相应的集合接口(集合) ,设置或映射)。这个
是因为这些接口的一般合约是用equals方法的
术语定义的,但是有序集合使用compareTo强加的等价
测试来代替equals。如果发生这种情况,这不是灾难性的
,但需要注意的是。



From 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.

For example, if you create a HashSet and add new BigDecimal("1.0") and new BigDecimal("1.00") to it, the set will contain two elements (because the values have different scales, so are non-equal according to equals and hashCode), but if you do the same thing with a TreeSet, the set will contain only one element, because the values compare as equal when you use compareTo.

Is there any specific reason behind this inconsistency?

解决方案

From the OpenJDK implementation of 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());
    }

More from the implementation:

 * <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.

Which is why the implementation of equals takes scale into consideration. The constructor that takes a string as a parameter is implemented like this:

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

where the third parameter will be used for the scale (in another constructor) which is why the strings 1.0 and 1.00 will create different BigDecimals (with different scales).

From Effective Java By Joshua Bloch:

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.

这篇关于为什么BigDecimal自然排序与equals不一致?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆