Java中BigDecimal的equals方法和compareTo方法的区别详析

 更新时间:2023年11月28日 11:09:01   作者:liweijackson  
这篇文章主要给大家介绍了关于Java中BigDecimal的equals方法和compareTo方法区别的相关资料,对于BigDecimal的大小比较,用equals方法的话会不仅会比较值的大小,还会比较两个对象的精确度,而compareTo方法则不会比较精确度,只比较数值的大小,需要的朋友可以参考下

前言

面试经常会问道为什么不能用BigDecimal的equals方法做等值比较?

其实BigDecimal的equals方法和compareTo并不一样,equals方法会比较两部分内容,分别是值(value)和标度(scale),而对于1.0和1这两个数字,他们的值虽然一样,但是精度是不一样的,在使用equals比较的时候会返回false。

知识扩展

BigDecimal,相信对于很多人来说都不陌生,很多人都知道他的用法,这是一种java.math包中提供的一种可以用来进行精确运算的类型。很多人都知道,在进行金额表示、金额计算等场景,不能使用double、float等类型,而是要使用对精度支持的更好的BigDecimal。所以,很多支付、电商、金融等业务中,BigDecimal的使用非常频繁。而且不得不说这是一个非常好用的类,其内部自带了很多方法,如加,减,乘,除等运算方法都是可以直接调用的。

BigDecimal的比较

阿里java开发手册也有规则要求比较不使用equals

【强制】 BigDecimal的等值比较应适应conpareTo()方法,而不是equals()。
说明:equals()方法会比较值和精度(1.0与1.00返回结果为false),二conpareTo()则会忽略精度

在做代码评审时见过下面的写法

if(bigDecimal == bigDecimal1){
	//两数相等
}

这种错误,相信聪明的读者一眼就可以看出问题,因为BigDecimal是对象,所以不能用 == 来判断两个数字的值是否相等。

上面代码的问题,在有一定的经验之后,还是可以避免的,我们可能首先可能会想到使用equals来比较,你觉得他有问题吗:

if(bigDecimal.equals(bigDecimal1)){
	//两数相等
}

以上这种写法,可能得到的结果和你预想的完全不一样!可以做个简单的测试运行一下代码

BigDecimal bigDecimal = new BigDecimal(1);
BigDecimal bigDecimal1 = new BigDecimal(1);
System.out.println(bigDecimal.equals(bigDecimal1));

BigDecimal bigDecimal2 = new BigDecimal(1);
BigDecimal bigDecimal3 = new BigDecimal(1.0);
System.out.println(bigDecimal2.equals(bigDecimal3));

BigDecimal bigDecimal4 = new BigDecimal("1");
BigDecimal bigDecimal5 = new BigDecimal("1.0");
System.out.println(bigDecimal4.equals(bigDecimal5));

输出结果为

true
true
false

BigDecimal的equals原理

通过上面的代码示例,我们发现,在使用BigDecimal的equals方法对1和1.0进行比较的时候,有的时候是true(当使用int、double定义BigDecimal时),有的时候是false(当使用String定义BigDecimal时)。

那么,为什么会出现这样的情况呢,我们先来看下BigDecimal的equals方法。在BigDecimal的JavaDoc中其实已经解释了其中原因:

Compares this  BigDecimal with the specified Object for equality.  Unlike compareTo, this method considers two 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)

大概意思就是,equals方法和compareTo并不一样,equals方法会比较两部分内容,分别是值(value)和标度(scale)。
代码如下:

所以,我们以上代码定义出来的两个BigDecimal对象(bigDecimal4和bigDecimal5)的标度是不一样的,所以使用equals比较的结果就是false了。

对代码进行debug,在debug的过程中我们也可以看到bigDecimal4的标度时0,而bigDecimal5的标度是1。到这里,我们大概解释清楚了,之所以equals比较bigDecimal4和bigDecimal5的结果是false,是因为标度不同

为什么标度不同

这个就涉及到BigDecimal的标度问题了,这个问题其实是比较复杂的,由于不是本文的重点,不做过多介绍

首先我们可以看到BigDecimal有4个构造方法

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

上面4种方法创建出来的BigDecimal标度时不相同的

BigDecimal(long) 和BigDecimal(int)

首先,最简单的就是BigDecimal(long) 和BigDecimal(int),因为是整数,所以标度就是0

    public BigDecimal(int val) {
        this.intCompact = val;
        this.scale = 0;
        this.intVal = null;
    }

    public BigDecimal(long val) {
        this.intCompact = val;
        this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
        this.scale = 0;
    }

BigDecimal(double)

而对于BigDecimal(double) ,当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候,其实创建出来的值并不是整好等于0.1的,而是0.1000000000000000055511151231257827021181583404541015625 。这是因为doule自身表示的只是一个近似值。

那么,无论我们使用new BigDecimal(0.1)还是new BigDecimal(0.10)定义,他的近似值都是0.1000000000000000055511151231257827021181583404541015625这个,那么他的标度就是这个数字的位数,即55。

在构造方法里面打断点可以验证

其他的浮点数也同样的道理。对于new BigDecimal(1.0)这样的形式来说,因为他本质上也是个整数,所以他创建出来的数字的标度就是0。所以,因为BigDecimal(1.0)和BigDecimal(1.00)的标度是一样的,所以在使用equals方法比较的时候,得到的结果就是true。

BigDecimal(String)

而对于BigDecimal(String) ,当我们使用new BigDecimal(“0.1”)创建一个BigDecimal 的时候,其实创建出来的值正好就是等于0.1的。那么他的标度也就是1。如果使用new BigDecimal(“0.10000”),那么创建出来的数就是0.10000,标度也就是5。

所以,因为BigDecimal(“1.0”)和BigDecimal(“1.00”)的标度不一样,所以在使用equals方法比较的时候,得到的结果就是false。

如何比较BigDecimal

在上面,我们解释了BigDecimal的equals方法,其实不只是会比较数字的值,还会对其标度进行比较。所以,当我们使用equals方法判断判断两个数是否相等的时候,是极其严格的。

那么,如果我们只想判断两个BigDecimal的值是否相等,那么该如何判断呢?

BigDecimal中提供了compareTo方法,这个方法就可以只比较两个数字的值,如果两个数相等,则返回0。

BigDecimal bigDecimal4 = new BigDecimal("1");
BigDecimal bigDecimal5 = new BigDecimal("1.0000");
System.out.println(bigDecimal4.compareTo(bigDecimal5));

以上代码,输出结果:

0

compareTo源码如下

附:bigdecimal判断是否为零(compareTo比较大小)

一、bigdecimal判断是否为零

1.判断Bigdecimal类型是否等于0的方法b.equals(BigDecimal.ZERO);

用equals方法和BigDecimal.ZERO进行比较。

2.上面判断是否等于零存在的问题:

我们来查看源代码:

  • Bigdecimal的equals方法不仅仅比较值的大小是否相等,首先比较的是scale(scale是bigdecimal的保留小数点位数,比如 new Bigdecimal(“1.001”),scale为3),也就是说,不但值得大小要相等,保留位数也要相等,equals才能返回true。
  • Bigdecimal b = new Bigdecimal(“0”) 和 Bigdecimal c = new Bigdecimal(“0.0”),用equals比较,返回就是false。
  • Bigdecimal.ZERO的scale为0。

所以,用equals方法要注意这一点。

3.我们还可以用一种方式判断Bigdecimal类型的值是否为0:

b.compareTo(BigDecimal.ZERO)==0,可以比较是否等于0,返回true则等于0,返回false,则不等于0

二、Bigdecimal的compareTo方法使用:

public static void main(String[] args) {
    BigDecimal bnum1, bnum2;

    bnum1 = new BigDecimal("10");
    bnum2 = new BigDecimal("20");

    int res = bnum1.compareTo(bnum2); 

    String str1 = "两个数相等";
    String str2 = "第一个数更大";
    String str3 = "第二个数更大";

    if( res == 0 )
        System.out.println( str1 );
    else if( res == 1 )
        System.out.println( str2 );
    else if( res == -1 )
        System.out.println( str3 );
    }
}

为什么比较返回值是0,-1和1呢? 我们去看看源代码!

根据源码中的三元运算符可以发现:

  • 情况1. 如果xs等于ys,则返回0。
  • 情况2. 如果xs不等于ys,则会执行另外一个三元运算符((xs > ys) ? 1 : -1)

这时候就会比较 xs 和 ys:

  • xs > ys 返回 1,
  • xs < ys 返回 -1。

因此得到结论!

总结 

到此这篇关于Java中BigDecimal的equals方法和compareTo方法区别的文章就介绍到这了,更多相关BigDecimal equals和compareTo方法区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaWeb导出Excel文件并弹出下载框

    JavaWeb导出Excel文件并弹出下载框

    这篇文章主要为大家详细介绍了JavaWeb导出Excel文件并弹出下载框的相关资料,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • 五分钟带你快速学习Spring IOC

    五分钟带你快速学习Spring IOC

    这篇文章主要给大家介绍了关于如何通过五分钟快速学习Spring IOC的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-03-03
  • MyBatis核心源码深度剖析SQL语句执行过程

    MyBatis核心源码深度剖析SQL语句执行过程

    这篇文章主要介绍了MyBatis核心源码深度剖析SQL执行过程,mybatis执行SQL的流程都是根据statement字符串从configuration中获取对应的mappedStatement,本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2022-05-05
  • Java如何在临界区中避免竞态条件

    Java如何在临界区中避免竞态条件

    这篇文章主要介绍了Java如何在临界区中避免竞态条件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • MySQL和Java通用加密解密方式小结

    MySQL和Java通用加密解密方式小结

    这篇文章主要介绍了MySQL和Java通用加密解密方式,加密方式使用AES加密,在转成Base64,本文结合实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2023-12-12
  • 深入解析Java的Spring框架中的混合事务与bean的区分

    深入解析Java的Spring框架中的混合事务与bean的区分

    这篇文章主要介绍了Java的Spring框架中的混合事务与bean的区分,Spring是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2016-01-01
  • Java.lang.ArrayIndexOutOfBoundsException的报错解决

    Java.lang.ArrayIndexOutOfBoundsException的报错解决

    Java.lang.ArrayIndexOutOfBoundsException是一个常见的错误,通常由于访问超出数组边界的索引值导致,本文就详细的介绍了解决方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-09-09
  • 通过JVM参数来优化垃圾回收性能方式

    通过JVM参数来优化垃圾回收性能方式

    这篇文章主要介绍了通过JVM参数来优化垃圾回收性能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-07-07
  • Spring拦截器实现鉴权的示例代码

    Spring拦截器实现鉴权的示例代码

    本文主要介绍了Spring拦截器实现鉴权的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java 中DateUtils日期工具类的实例详解

    Java 中DateUtils日期工具类的实例详解

    这篇文章主要介绍了Java 中DateUtils日期工具类的实例详解的相关资料,有时候开发java项目使用日期类型,这里介绍下日期工具类,需要的朋友可以参考下
    2017-08-08

最新评论