【Java开发日记】为什么不推荐使用 BigDecimal 的 equals 方法做等值比较 ?

前言

BigDecimal 是 java.math 包中提供的一种可以用来进行精确运算的类型。所以,在支付、电商等业务中,BigDecimal 的使用非常频繁。而且其内部自带了很多方法,如加,减,乘,除等运算方法都是可以直接调用的。除了需要用 BigDecimal 表示数字和进行数字运算以外,代码中还经常需要对于数字进行相等判断。
说到等值比较,先看看《阿里巴巴Java开发手册》中的要求👇:

那么为什么会有这样的要求呢🤔~ 其中的奥秘是什么呢🤔~ 

BigDecimal 做等值比较

public static void main(String[] args) {
    BigDecimal bigDecimal1 = new BigDecimal(1);
    BigDecimal bigDecimal2 = new BigDecimal(1);
    if(bigDecimal1 == bigDecimal2){
        //等值比较
    }
}

可以看出来上面的代码是有问题的,因为 BigDecimal 是对象,不能使用 == 来做等值判断。
如果使用 BigDecimal 的 equals 方法做等值比较是不是可以呢?👇

public static void main(String[] args) {
    BigDecimal bigDecimal1 = new BigDecimal(1);
    BigDecimal bigDecimal2 = new BigDecimal(1);
    if(bigDecimal1.equals(bigDecimal2)){
        //等值比较
    }
}

这里先跑代码来看看能不能用 BigDecimal 的 equals 方法做等值比较,

public static void main(String[] args) {
    BigDecimal bigDecimal1 = new BigDecimal(1);
    BigDecimal bigDecimal2 = new BigDecimal(1);
    System.out.println(bigDecimal1.equals(bigDecimal2));
    BigDecimal bigDecimal3 = new BigDecimal(1);
    BigDecimal bigDecimal4 = new BigDecimal(1.0);
    System.out.println(bigDecimal3.equals(bigDecimal4));
    BigDecimal bigDecimal5 = new BigDecimal("1");
    BigDecimal bigDecimal6 = new BigDecimal("1.0");
    System.out.println(bigDecimal5.equals(bigDecimal6));
}

可以发现,在使用 BigDecimal 的 equals 方法对 1 和 1.0 进行比较的时候:使用 int、double 定义 BigDecimal 结果是 true;使用 String 定义 BigDecimal 结果是false,为什么会出现这种情况呢?
一起来看看 equals 方法的源码 👇

/**
 * 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());
}

其实从方法的注释中就能找到答案:equals 方法会比较两部分内容,分别是值(value)和标度(scale),也就是说 bigDecimal5 和 bigDecimal6 的值虽然相同,但是标度是不一样的。
打个断点,debug 一下看看~

可以看见 bigDecimal5 的标度值是0,而bigDecimal6的标度值是1,所以 bigDecimal5 和 bigDecimal6 的比较结果是false (●ˇ∀ˇ●)
那么这时候又产生了一个疑问:为什么标度不同呢?🤔
BigDecimal 一共有以下 4 个构造方法:

  • BigDecimal(int)
  • BigDecimal(double)
  • BigDecimal(long)
  • BigDecimal(String)

其中最容易理解的就是 BigDecimal(int)和 BigDecimal(long),因为是整数,所以标度就是 0 (源码如下👇):

/**
 * Translates an {@code int} into a {@code BigDecimal}.  The
 * scale of the {@code BigDecimal} is zero.
 *
 * @param val {@code int} value to be converted to
 *            {@code BigDecimal}.
 * @since  1.5
 */
public BigDecimal(int val) {
    this.intCompact = val;
    this.scale = 0;
    this.intVal = null;
}
/**
 * Translates a {@code long} into a {@code BigDecimal}.  The
 * scale of the {@code BigDecimal} is zero.
 *
 * @param val {@code long} value to be converted to {@code BigDecimal}.
 * @since  1.5
 */
public BigDecimal(long val) {
    this.intCompact = val;
    this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
    this.scale = 0;
}

而对于 BigDecimal (double)来说,当使用 new BigDecimal (0.1)创建一个对象的时候,其实创建出来的对象的值并不是等于0.1,而是等于0.1000000000000000055511151231257827021181583404541015625

再打个断点,debug一下看看标度值是多少

可以看到标度值是55,这个值是怎么来的呢?其实很简单,这个标度值就是这个数字的位数,其他的浮点数也同样的道理。对于 new BigDecimal (1.0),和new BigDecimal (1.00) 这样的形式来说,因为他本质上也是个整数,所以他创建出来的数字的标度就是0。
最后再看看 BigDecimal(String) ,当使用 new BigDecimal ("0.1") 创建一个 BigDecimal 的时候,其实创建出来的值正好就是等于 0.1 的。那么他的标度也就是 1;如果使用 new BigDecimal("0.10000"),那么创建出来的数就是 0.10000,标度也就是 5。
到这里相信各位小伙伴也明白了为什么 bigDecimal5 和 bigDecimal6 用equals 方法做等值比较的结果是false了。
如果只想判断两个 BigDecimal 的值是否相等,那么该如何判断呢?
在 BigDecimal 中也提供了一个方法 —— compareTo 方法,这个方法就可以只比较两个数字的值,如果两个数相等,则返回 0。

把 equals 换成 compareTo 后可以发现,bigDecimal5 和 bigDecimal6 等值比较的结果是0,也就是说明这二者的值是相等的。
P.S. 所以在做等值比较的时候不要随便用 BigDecimal 的 equals 方法,如果只是要对数值作比较,就果断选择 compareTo 方法。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

评论 79
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小假

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值