阿里巴巴的Java开发手册中,禁止使用BigDecimal的equals方法做等值比较的,原因是因为BigDecimal的equals方法在比较时会考虑精度,而不仅仅是数值的相等性。这可能会导致一些意外的结果,特别是在涉及到货币计算等需要精确计算的场景中。
文档说明
【强制】如上所示BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法。 说明:equals()方法会比较值和精度(1.0与1.00返回结果为false),而compareTo()则会忽略精度。
案例分析
下面我将通过一个代码案例来说明这个问题。
假设我们有两个BigDecimal对象,分别表示0.1和0.10这两个数值:
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.10");
System.out.println(num1.equals(num2));
System.out.println(num1.compareTo(num2));
返回结果
false
0
BigDecimal equals返回结果说明
如果我们使用equals方法进行等值比较,根据BigDecimal的equals方法的实现,它会考虑数值的精度。因此,num1.equals(num2)的结果将会是false,即使这两个数值在数值上是相等的。
这是因为BigDecimal的equals方法会比较数值的精度,即小数点后的位数。在上面的例子中,num1和num2的精度分别是1和2,因此它们被认为是不相等的。
BigDecimal compareTo返回结果说明
- 如果返回值为0,表示两个
BigDecimal对象的数值相等。 - 如果返回值小于0,表示调用
compareTo方法的对象小于被比较的对象。 - 如果返回值大于0,表示调用
compareTo方法的对象大于被比较的对象。
compareTo方法只比较数值的大小,而不考虑精度。因此,即使num1和num2的精度不同,它们的数值相等时,compareTo方法仍然会返回0,
在上面的代码中,num1.compareTo(num2)返回的结果是0,这意味着num1和num2的数值是相等的。
BigDecimal equals源码分析
/**
* 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.inflated().equals(xDec.inflated());
}
根据BigDecimal equals源码注释说明可以看出
BigDecimal与指定的Object进行相等性比较。与compareTo不同,此方法仅当两个BigDecimal对象的值和小数位数相等时才认为它们相等(因此,使用此方法进行比较时,2.0不等于2.00)。
源码中 if (scale != xDec.scale) 就是对精度的比较
BigDecimal compareTo源码分析
/**
* Compares this {@code BigDecimal} with the specified
* {@code BigDecimal}. Two {@code BigDecimal} objects that are
* equal in value but have a different scale (like 2.0 and 2.00)
* are considered equal by this method. This method is provided
* in preference to individual methods for each of the six boolean
* comparison operators ({@literal <}, ==,
* {@literal >}, {@literal >=}, !=, {@literal <=}). The
* suggested idiom for performing these comparisons is:
* {@code (x.compareTo(y)} <<i>op</i>> {@code 0)}, where
* <<i>op</i>> is one of the six comparison operators.
*
* @param val {@code BigDecimal} to which this {@code BigDecimal} is
* to be compared.
* @return -1, 0, or 1 as this {@code BigDecimal} is numerically
* less than, equal to, or greater than {@code val}.
*/
public int compareTo(BigDecimal val) {
// Quick path for equal scale and non-inflated case.
if (scale == val.scale) {
long xs = intCompact;
long ys = val.intCompact;
if (xs != INFLATED && ys != INFLATED)
return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
}
int xsign = this.signum();
int ysign = val.signum();
if (xsign != ysign)
return (xsign > ysign) ? 1 : -1;
if (xsign == 0)
return 0;
int cmp = compareMagnitude(val);
return (xsign > 0) ? cmp : -cmp;
}
根据BigDecimal compareTo源码注释说明可以看出
此BigDecimal与指定的BigDecimal进行比较。该方法将具有相同值但具有不同标度(例如2.0和2.00)的两个BigDecimal对象视为相等。
最后总结
通过使用compareTo方法,我们可以得到正确的等值比较结果,而不会受到BigDecimal的精度考虑的影响。
结合以上分析来看,阿里巴巴的Java开发手册,禁止使用BigDecimal的equals方法做等值比较。是为了避免在精确计算的场景中出现意外的结果。通过使用compareTo方法进行等值比较,可以得到正确的比较结果。这样可以保证代码的准确性和可靠性,特别是在涉及到货币计算等需要精确计算的场景中。