来自Javadoc的BigDecimal
:
注:如果BigDecimal
对象用作SortedMap
中的键或SortedSet
中的元素,则应小心操作,因为BigDecimal
的自然顺序与等于的顺序不一致。
例如,如果您创建一个HashSet
并向其中添加新的BigDecimal(“1.0”)
和新的BigDecimal(“1.00”)
,则该集合将包含两个元素(因为值具有不同的比例,因此根据equals
和hashCode
,它们是不相等的),但是,如果对树集执行相同的操作,则该集将只包含一个元素,因为使用
compareTo
时,这些值的比较是相等的。
这种不一致背后有什么具体原因吗?
BigDecimal的工作原理是有两个数字,一个整数和一个刻度。整数是“数字”,刻度是小数点右边的位数。基本上是一个以10为基数的浮点数。
当你说“1.0”
和“1.00”
时,这两个值在技术上是不同的,用大十进制表示法:
1.0
integer: 10
scale: 1
precision: 2
= 10 x 10 ^ -1
1.00
integer: 100
scale: 2
precision: 3
= 100 x 10 ^ -2
在科学记数法中,这两种方法你都不会做,它应该是1x10^0
或者仅仅是1
,但bigdecime允许这样做。
在compareTo
中,忽略刻度,并将其作为普通数字进行计算,1==1
。在equals
中,比较整数和刻度值,10!=100
和1!=2
。BigDecimal equals方法忽略了我假设的object==这个
检查,因为其目的是将每个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的精度()方法时有一个半警告,如果BigDecimal为0,该方法总是返回1。在这种情况下比较
我认为BigDecimal允许尾随零是很奇怪的,但这基本上是必要的。执行像“1.1”“1.01”
这样的数学运算需要转换,但“1.10”“1.01”
不需要。
因此,compareTo
将大小数作为数字进行比较,equals
将大小数作为大小数进行比较。
如果不需要进行比较,请在不重要的地方使用列表或数组。HashSet和TreeSet当然是专门为保存独特元素而设计的。
在算术精度方面,这种行为似乎是合理的,其中尾随零是有效数字,1.0与1.00的含义不同。让他们不平等似乎是一个合理的选择。
然而,从比较的角度来看,两者都不大于或小于另一个,可比接口需要一个总顺序(即每个BigDecimal必须与任何其他BigDecimal可比)。这里唯一合理的选择是定义一个总的顺序,这样compareTo方法将认为两个数字相等。
请注意,只要有文档记录,equal和compareTo之间的不一致就不是问题。它甚至有时正是一个人所需要的。
从BigDecimal的OpenJDK实现中:
/**
* 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
将创建不同的大小数(具有不同的比例)。
来自约书亚·布洛赫的有效Java:
compareTo合同的最后一段是一个强烈的建议,而不是一个真实的条款,它简单地说明了compareTo方法施加的平等测试通常应返回与equals方法相同的结果。如果遵守这一规定,则称compareTo方法施加的顺序与equals一致。如果它被违反,则称其顺序与equals不一致。如果类的compareTo方法施加的顺序与equals不一致,则该类仍然可以工作,但包含该类元素的已排序集合可能不遵守相应集合接口(集合、集合或映射)的一般约定。这是因为这些接口的一般契约是根据equals方法定义的,但排序集合使用compareTo施加的相等测试来代替equals。如果发生这种情况,这不是一场灾难,但这是需要注意的。
休假后回来:)带着问题。我正在阅读比较接口留档从比较文档。我明白,我们使用可比,因为它将为我们提供排序和自然排序。在留档中,它被写成。 强烈建议(尽管不是必需的)自然顺序与equals一致。这是因为没有显式比较器的排序集(和排序映射)在与自然顺序与equals不一致的元素(或键)一起使用时表现“奇怪”。特别是,这样的排序集(或排序映射)违反了用equals方法定义的set(或映射)的一般约定。 “
我可以理解,在大小数中,2.0并不等于2.00,因为2.00实际上使用了更高精度的数字。我很难理解为什么不被认为等于,因为这两个数字实际上使用相同数量的精确数字。依我看,它们只是完全相同数字的不同表示。 我知道,我可以使用和而不是,我只是对这背后的推理感兴趣。有人能帮我理解吗?
问题内容: 实际上,我已经找到了可能的解决方案 当然,可以通过使比较更加健壮的方法来改进它,但是问题是此技术是否可以接受或是否有更好的解决方案? 如果有人知道为什么Java设计人员决定以这种方式实现BigDecimal的equals,那么阅读它会很有趣。 问题答案: 来自BigDecimal的Javadoc 等于 将其与指定的相等性进行比较。与之不同的是,此方法 仅在 两个对象的 值和比例 相等
问题内容: 我已经为此工作了几个月。我只是无法获得(真实的字母数字)结果。令我震惊的是我无法获得自1992年以来的成就。 我正在寻找SQL,VBS或简单的excel或access中的任何解决方案。这是我的数据: 我要查找的顺序是真实的字母数字顺序,如下所示: 库存为7800条记录,因此我在处理能力方面也遇到了一些问题。 任何帮助,将不胜感激。 杰夫 问题答案: 在本机Excel中,您可以添加多个排
问题内容: 什么是自然排序。假设我有一个Employee对象,其名称,年龄和加入日期按什么是自然顺序排序? 问题答案: 自然排序是一种字母数字种类,对人类而言似乎是自然的。 在经典的字母数字排序中,我们将具有以下内容: 1 10 11 12 2 20 21 3 4 5 6 7 如果您使用自然排序,则将为: 根据语言的不同,自然排序有时会忽略大写字母并加重字母(即,所有重音字母都被视为非重音字母)。
问题内容: 请查看下面的代码: 有人可以解释一下为什么输出是 代替 ? 因为在API中它表示优先级队列的元素是根据其自然顺序进行排序的。 问题答案: PriorityQueue基于优先级堆。尽管未对元素进行排序,但此数据结构允许非常快地检索最小元素。将元素添加到PriorityQueue的速度比向基于树的TreeSet快。由于未对元素进行排序,因此如API所述,迭代器“不会以任何特定顺序返回元素”