这不是关于如何比较两个BigDecimal
对象的问题 - 我知道你可以使用compareTo
而不是equals
这样做,因为equals
记录为:
与compareTo不同,此方法仅考虑两个BigDecimal对象的值和比例相等(因此通过此方法比较时2.0不等于2.00).
问题是:为什么equals
以这种看似违反直觉的方式指定了?也就是说,为什么能够区分2.0和2.00 很重要?
似乎必须有这样的理由,因为Comparable
指定compareTo
方法的文档指出:
强烈建议(尽管不要求)自然排序与equals一致
我想必须有一个很好的理由忽略这个建议.
Oli*_*rth 25
因为在某些情况下,精度指示(即误差范围)可能很重要.
例如,如果您要存储由两个物理传感器进行的测量,那么一个传感器的精度可能比另一个高10倍.代表这一事实可能很重要.
一般规则equals
是两个相等的值应该可以相互替代。也就是说,如果使用一个值执行计算给出了一些结果,那么将一个equals
值代入相同的计算应该给出第一个结果equals
的结果。这适用于那些值的对象,例如String
,Integer
,BigDecimal
等。
现在考虑BigDecimal
值 2.0 和 2.00。我们知道它们在数值上相等,并且compareTo
它们返回 0。但equals
返回 false。为什么?
这是它们不可替代的示例:
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
Run Code Online (Sandbox Code Playgroud)
结果显然不相等,因此 的值a
不可替代b
。所以,a.equals(b)
应该是假的。
在任何其他答案中尚未考虑的一点equals
是,要求与123.00相同,并且为123.0产生相同值所需hashCode
的hashCode
实施成本(但仍然做得合理区分不同的值)将远远大于不需要这样做的hashCode实现.在当前语义下,hashCode
需要乘以31并为每个32位的存储值添加.如果hashCode
要求在具有不同精度的值之间保持一致,则要么必须计算任何值的标准化形式(昂贵),要么至少执行类似计算值的基数-9999999999数字根并将其相乘, mod 999999999,基于精度.这种方法的内部循环是:
temp = (temp + (mag[i] & LONG_MASK) * scale_factor[i]) % 999999999;
Run Code Online (Sandbox Code Playgroud)
用64位模数运算代替乘以31 - 更加昂贵.如果想要一个将数值等BigDecimal
价值视为等价的哈希表,并且找到表中所寻找的大多数键,那么实现所需结果的有效方法是使用存储值包装器的哈希表,而不是直接存储值.要在表中查找值,请首先查找值本身.如果未找到,则将值标准化并查找该值.如果找不到任何内容,请创建一个空包装器并在数字的原始和规范化形式下存储条目.
寻找不在表中并且之前未被搜索过的东西需要昂贵的标准化步骤,但寻找已经搜索过的东西会快得多.相比之下,如果HashCode需要返回数字的等价值,由于精度不同,它们的存储方式完全不同,这会使所有哈希表操作都慢得多.
在数学上,10.0 等于 10.00。在物理学中,10.0m 和 10.00m 可以说是不同的(不同的精度),当谈到 OOP 中的对象时,我肯定会说它们不相等。
如果 equals 忽略了比例,也很容易想到意外的功能(例如:如果 a.equals(b),你会不会期望 a.add(0.1).equals(b.add(0.1)?)。
归档时间: |
|
查看次数: |
12899 次 |
最近记录: |