注意:如果将
BigDecimal
个对象用作SortedMap
中的键或SortedSet
中的元素,则应谨慎行事BigDecimal
自然排序 与equals 不一致。
例如,如果您创建HashSet
并向其添加new BigDecimal("1.0")
和new BigDecimal("1.00")
,则该集合将包含两个元素(因为这些值具有不同的比例,因此不相等根据{{1}}和equals
),但是如果你使用hashCode
执行相同的操作,则该集合将只包含一个元素,因为当您使用{{1 }}
这种不一致背后是否有任何具体原因?
答案 0 :(得分:8)
来自BigDecimal的OpenJDK implementation:
/**
* 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.0
和1.00
将创建不同的BigDecimals(具有不同的比例)。
来自 Effective Java 作者:Joshua Bloch:
compareTo合约的最后一段,这是一个强大的 建议而不是真正的规定,只是说明了 compareTo方法强加的相等测试一般应该返回 与equals方法相同的结果。如果遵守这一规定, compareTo方法强加的排序被认为是一致的 与平等。如果它被违反,则说明顺序不一致 与平等。 compareTo方法强加订单的类 与equals不一致仍然有效,但排序集合 包含该类的元素可能不遵守一般合同 适当的集合接口(Collection,Set或Map)。这个 是因为这些接口的一般合同是在中定义的 equals方法的术语,但已排序的集合使用相等 由compareTo代替等于的测试。这不是一场灾难 如果发生这种情况,但需要注意的事项。
答案 1 :(得分:3)
在算术精度的上下文中,行为似乎是合理的,其中尾随零是significant figures而1.0不具有与1.00相同的含义。使它们不平等似乎是一个合理的选择。
然而,从比较的角度来看,两者都不大于或小于另一个,并且Comparable接口需要总顺序(即每个BigDecimal必须与任何其他BigDecimal相当)。这里唯一合理的选择是定义一个总订单,这样compareTo方法会认为这两个数字相等。
请注意,只要记录,等于和compareTo之间的不一致就不是问题。甚至有时exactly what one needs。
答案 2 :(得分:2)
BigDecimal通过两个数字,一个整数和一个比例来工作。整数是“数字”,标度是小数点右边的位数。基本上是10个浮点数。
当您说"1.0"
和"1.00"
时,这些在BigDecimal表示法中的技术上是不同的值:
1.0
integer: 10
scale: 1
precision: 2
= 10 x 10 ^ -1
1.00
integer: 100
scale: 2
precision: 3
= 100 x 10 ^ -2
在科学记数法中你不会做其中任何一个,它应该是1 x 10 ^ 0
或只是1
,但BigDecimal允许它。
在compareTo
中,忽略比例,并将它们评估为普通数字1 == 1
。在equals
中,比较整数和比例值,10 != 100
和1 != 2
。 BigDecimal equals方法忽略我假设的object == this
检查,因为意图是每个BigDecimal被视为一种数字,而不是对象。
我想把它比作:
// same number, different types
float floatOne = 1.0f;
double doubleOne = 1.0;
// true: 1 == 1
System.out.println( (double)floatOne == doubleOne );
// also compare a float to a double
Float boxFloat = floatOne;
Double boxDouble = doubleOne;
// false: one is 32-bit and the other is 64-bit
System.out.println( boxInt.equals(boxDouble) );
// BigDecimal should behave essentially the same way
BigDecimal bdOne1 = new BigDecimal("1.0");
BigDecimal bdOne2 = new BigDecimal("1.00");
// true: 1 == 1
System.out.println( bdOne1.compareTo(bdOne2) );
// false: 10 != 100 and 1 != 2 ensuring 2 digits != 3 digits
System.out.println( bdOne1.equals(bdOne2) );
因为BigDecimal允许特定的“精度”,所以比较整数和比例与比较数字和精度大致相同。
虽然在谈论BigDecimal的precision()方法时有一个半谬论,如果BigDecimal为0,它总是返回1.在这种情况下,compareTo&amp;&amp; precision计算true,equals计算false。但0 * 10 ^ -1
不应该等于0 * 10 ^ -2
,因为前者是2位数0.0
,后者是3位数0.00
。 equals方法是比较值和数字。
我认为BigDecimal允许尾随零是奇怪的,但这基本上是必要的。像"1.1" + "1.01"
这样的数学运算需要转换,但"1.10" + "1.01"
不需要转换。
因此compareTo
将BigDecimals与数字进行比较,equals
将BigDecimals与BigDecimals进行比较。
如果比较是不需要的,请使用无关紧要的List或数组。 HashSet和TreeSet当然是专门为保存独特元素而设计的。
答案 3 :(得分:2)
答案很简短。 equals()方法比较对象,而compareTo()比较值。在BigDecimal的情况下,不同的对象可以表示相同的值。这就是为什么equals()可能返回false,而compareTo()返回0。
等于对象=&gt;等值
相等的值= /&gt;平等对象
对象只是某个真实世界值的计算机表示。例如,相同的图片可能以GIF和JPEG格式表示。这非常像BigDecimal,其中相同的值可能具有不同的表示。