先看看几行代码:
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimal test = new BigDecimal("0.0");
System.out.println("equals 方法判断是否为0:" +test.equals(BigDecimal.ZERO));
System.out.println("compareTo方法判断是否为0:" + (test.compareTo(BigDecimal.ZERO) == 0));
}
}
其运行结果让我很是诧异,本来我以为两个都是true,但是结果并不是这样:
"C:\Program Files\Java\jdk1.8.0_171\bin\java.exe""......
equals 方法判断是否为0:false
compareTo方法判断是否为0:true
Process finished with exit code 0
在我看来equals方法和compareTo方法都是用来比较对象,那么针对相同对象进行相等性判断应该是一样的。但是结果为什么会出现这种差异性呢?
先看一下关键代码,equals和compareTo两个方法的实现和注释。
/**
* 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());
}
注释描述翻译: 该方法用于与指定对象比较相等性,与CompareTo不同,这个方法认为两个BigDecimal对象的数值(value)和标度(scale)均相等的情况才相等(该方法比较2.0和2.00时结果是不相等的)。
简单看一下理一下判断流程:
BigDecimal
,不是则直接返回false
==
比较两个对象的hashCode
,如果相等直接返回true
scale
),不相等则返回false
intCompact
值,并判断其合法性,如有需要会调用compactValFor
方法获取intVal
中的值,然后进行比较。如果使用compactValFor
方法也无法获取到有效数字,进行下一步。intVal
比较,intVal
的类型为BigInteger
,其比较结果为BigInteger
的equals
方法的比较结果。关于流程中的几个变量和方法说明后面会有相应说明。
/**
* 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
对象。该方法中在判断对象相等时只需要考虑数值上的相等性而不需要考虑其标度。这一点是equals
是有很大的区别。
简单看一下理一下判断流程:
intCompact
均有效的情况下,直接比较intCompact
的相等性。比较流程是连续使用三目运算符先判断是否相等,然后判断大小。 /**
* The unscaled value of this BigDecimal, as returned by {@link
* #unscaledValue}.
*
* @serial
* @see #unscaledValue
*/
private final BigInteger intVal;
/**
* Sentinel value for {@link #intCompact} indicating the
* significand information is only available from {@code intVal}.
*/
static final long INFLATED = Long.MIN_VALUE;
/**
* If the absolute value of the significand of this BigDecimal is
* less than or equal to {@code Long.MAX_VALUE}, the value can be
* compactly stored in this field and used in computations.
*/
private final transient long intCompact;
/**
* Returns the compact value for given {@code BigInteger}, or
* INFLATED if too big. Relies on internal representation of
* {@code BigInteger}.
*/
private static long compactValFor(BigInteger b) {
int[] m = b.mag;
int len = m.length;
if (len == 0)
return 0;
int d = m[0];
if (len > 2 || (len == 2 && d < 0))
return INFLATED;
long u = (len == 2)?
(((long) m[1] & LONG_MASK) + (((long)d) << 32)) :
(((long)d) & LONG_MASK);
return (b.signum < 0)? -u : u;
}
/**
* Returns appropriate BigInteger from intVal field if intVal is
* null, i.e. the compact representation is in use.
*/
private BigInteger inflated() {
if (intVal == null) {
return BigInteger.valueOf(intCompact);
}
return intVal;
}
注释描述翻译:
intVal
变量:不包含标度的有效数字整数值。intCompact
变量:BigDecimal
的有效数字的绝对值,如果该值小于等于Long.MAX_VALUE
,则存储在在这个字段中,并用于计算。INFLATED
常量:intCompact
的标准值,一般是当前对象的数值过大,intCompact
无法存储该数据的时候,就会将intCompact
数值为该值。compactValFor
方法:用于返回intVal
中的数字,如果数字过大则会返回INFLATED
。inflated
方法:用于获取intVal
变量。根据上面的描述可以看出intCompact
在BigDecimal对象中的重要性,BigDecimal
中有两个变量特别重要:intCompact
和scala
。这两个值可以就是科学记数法(a×10^n)中的的基数a (intcompact)和标度n(scala)。
思考:
intCompact
为常量,同时使用transient
关键字标记。为什么这个变量需要transient
标记呢?其实通过两个方法的注释就能看出,通过两个方法进行相等性判断产生不同结果的根本原因是对标度的判断。至于为什么这么做,其原因个人认为主要是内部采用了科学记数法的缘故吧。科学技术中基数相等,标度不一样的情况,也是被认为是不相等的。
全部评论