Java 为什么指定BigDecimal.equals来分别比较值和比例?
这不是关于如何比较两个Java 为什么指定BigDecimal.equals来分别比较值和比例?,java,bigdecimal,Java,Bigdecimal,这不是关于如何比较两个BigDecimal对象的问题-我知道可以使用compareTo而不是equals来进行比较,因为equals被记录为: 与compareTo不同,此方法仅当两个BigDecimal对象的值和比例相等时才认为它们相等(因此,使用此方法进行比较时,2.0不等于2.00) 问题是:equals为什么以这种看似违反直觉的方式指定?也就是说,为什么能够区分2.0和2.00非常重要 这似乎有可能是有原因的,因为指定了compareTo方法的Comparable文档说明: 强烈建议(尽
BigDecimal
对象的问题-我知道可以使用compareTo
而不是equals
来进行比较,因为equals
被记录为:
与compareTo不同,此方法仅当两个BigDecimal对象的值和比例相等时才认为它们相等(因此,使用此方法进行比较时,2.0不等于2.00)
问题是:equals
为什么以这种看似违反直觉的方式指定?也就是说,为什么能够区分2.0和2.00非常重要
这似乎有可能是有原因的,因为指定了compareTo
方法的Comparable
文档说明:
强烈建议(尽管不是必需的)自然顺序与equals一致
我想一定有充分的理由忽略这一建议。因为在某些情况下,精度指示(即误差幅度)可能很重要
例如,如果您存储由两个物理传感器进行的测量,可能其中一个比另一个精确10倍。代表这一事实可能很重要。在数学中,10.0等于10.00。在物理学中,10.0m和10.00m可以说是不同的(精度不同),当在OOP中谈论对象时,我肯定会说它们不相等
如果equals忽略了刻度(例如:如果a.equals(b),您不希望a.add(0.1)equals(b.add(0.1)?)吗?如果数字被四舍五入,它会显示计算的精度,换句话说:
- 10.0可能意味着确切的数字在9.95和10.05之间
- 10.00可能意味着准确的数字在9.995和10.005之间
换句话说,它链接到。compareTo方法知道尾随的零不会影响由
BigDecimal
表示的数值,这是compareTo
唯一关心的方面。相比之下,equals
方法通常无法知道对象的哪些方面如果程序员可能感兴趣的两个对象在各个方面都是等价的,那么只需要返回true
。如果x.equals(y)
是真的,那么x.toString().equals(y.toString())
会产生false,这是相当令人惊讶的
另一个可能更重要的问题是,BigDecimal
本质上结合了一个biginger
和一个比例因子,这样,如果两个数字代表相同的值,但尾随零的数量不同,那么其中一个将持有一个biginger
,其值是另一个的十倍幂质量要求尾数和刻度都匹配,那么BigDecimal
的hashCode()
可以使用BigInteger
的哈希代码。如果两个值可能被视为“相等”尽管它们包含不同的BigInteger
值,但这会使事情变得非常复杂。使用自己的备份存储而不是BigInteger
的BigDecimal
类型可以通过多种方式实现,以允许数字以表示相同的数字会比较相等(作为一个简单的例子,一个版本在每个long
值中包含九个十进制数字,并且始终要求小数点位于九个组之间,可以以忽略值为零的后续组的方式计算哈希代码)但是封装了biginger
的BigDecimal
不能做到这一点
我想一定有充分的理由忽视这一建议
也许不是。我提出了一个简单的解释,BigDecimal
的设计者只是做了一个糟糕的设计选择
compariable
与相等一致BigDecimal
类(在后台使用Java的BigDecimal
实现)做出了相反的选择:
BigDecimal("2.0") == BigDecimal("2.00") // true
在任何其他答案中尚未考虑的一点是,
equals
需要与hashCode
保持一致,并且hashCode
实现的成本需要产生123.0与123.00相同的值(但仍然要合理地区分不同的值)在目前的语义下,hashCode
需要对每32位存储值进行乘法31和加法运算。如果hashCode
需要在不同精度的值之间保持一致,则必须计算no任何值的rmalized形式(昂贵),或者至少,执行类似于计算值的基数-99999999数字根的操作,并根据精度将其乘以mod 99999999。这种方法的内部循环是:
temp = (temp + (mag[i] & LONG_MASK) * scale_factor[i]) % 999999999;
用64位模运算替换乘31运算——代价要昂贵得多。如果想要一个哈希表,它将数值等效的BigDecimal
值视为等效的,并且大多数键
var a = new BigDecimal("2.0");
var b = new BigDecimal("2.00");
var three = new BigDecimal(3);
a.divide(three, RoundingMode.HALF_UP)
==> 0.7
b.divide(three, RoundingMode.HALF_UP)
==> 0.67