为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?

△一个对Coding有着独特追求的人△

作者 l Hollis

来源 l Hollis(ID:hollischuang)

BigDecimal,相信对于很多人来说都不陌生,很多人都知道他的用法,这是一种java.math包中提供的一种可以用来进行精确运算的类型。

很多人都知道,在进行金额表示、金额计算等场景,不能使用double、float等类型,而是要使用对精度支持的更好的BigDecimal。

所以,很多支付、电商、金融等业务中,BigDecimal的使用非常频繁。而且不得不说这是一个非常好用的类,其内部自带了很多方法,如加,减,乘,除等运算方法都是可以直接调用的。

除了需要用BigDecimal表示数字和进行数字运算以外,代码中还经常需要对于数字进行相等判断。

关于BigDecimal等值判断的这个知识点,在最新版的《阿里巴巴Java开发手册》中也有说明:


那么,为什么会有这样的要求呢?背后的思考是什么呢?

其实,我在之前的CodeReview中,看到过以下这样的低级错误:

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

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

以上这种问题,在有一定的经验之后,还是可以避免的,但是聪明的读者,看一下以下这行代码,你觉得他有问题吗:

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));

以上代码,输出结果为:

truetruefalse

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,是因为精度不同。

那么,为什么精度不同呢?为什么bigDecimal2和bigDecimal3的精度是一样的(当使用int、double定义BigDecimal时),而bigDecimal4和bigDecimal5却不一样(当使用String定义BigDecimal时)呢?

为什么精度不同

这个就涉及到BigDecimal的精度问题了,这个问题其实是比较复杂的,由于不是本文的重点,这里面就简单介绍一下吧。大家感兴趣的话,后面单独讲。

首先,BigDecimal一共有以下4个构造方法:

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

以上四个方法,创建出来的的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(double) ,当我们使用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

其源码如下:

总结

BigDecimal是一个非常好用的表示高精度数字的类,其中提供了很多丰富的方法。

但是,他的equals方法使用的时候需要谨慎,因为他在比较的时候,不仅比较两个数字的值,还会比较他们的精度,只要这两个因素有一个是不相等的,那么结果也是false、

如果读者想要对两个BigDecimal的数值进行比较的话,可以使用compareTo方法。


往期推荐

阿里《Java开发手册》最新嵩山版发布!


多图证明,Java到底是值传递还是引用传递?


URL 去重的 6 种方案!(附详细代码)


关注下方二维码,收获更多干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545826.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动图演示:手撸堆栈的两种实现方法!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)正式开始之前,先和各位朋友聊聊公众号后期的一些打算,后面的文章计划写一些关于数据结构和算法的内容…

多图带你彻底理解Java中的21种锁!

作者 | 悟空聊架构来源 | 悟空聊架构(ID:PassJava666)本篇主要内容如下:本篇主要内容本篇文章已收纳到我的Java在线文档、 Github我的SpringCloud实战项目持续更新中帮你总结好的锁:序号锁名称应用1乐观锁CAS2悲观锁sy…

杨辉三角——数组解决

杨辉三角如图下所示,每一行的第一个数和最后一个数都为1,每一行中间的数(出去第一个和最后一个)a等于上一行与其相同列数的数b与数b前面的数之和。例: 第3行第2列的数是3,它就等于第2行第2列的数&#xff…

VS生成的exe文件如何在其他电脑上运行

在VS编译器上编写的程序都会生成一个exe文件,有时候写了一个很装逼的程序想在别人电脑炫耀一下,奈何将这个exe文件拷贝过去并不能运行,直接宣告装逼失败。为此将介绍一下如何将生成的exe文件在其他电脑上运行,步骤如下&#xff1a…

netty websocket 简单消息推送demo

2019独角兽企业重金招聘Python工程师标准>>> 今天心情很不好!!! 原因保密。 这篇是基于"netty与websocket通信demo"。 错误想法:大量客户请求,共用一个worker,来实现推送。 正确作法&…

给 JDK 官方提了一个 Bug,结果...

图 by:石头北京-望京关于作者:程序猿石头(ID: tangleithu),现任阿里巴巴技术专家,清华学渣,前大疆后端 Leader。背景分享一下之前踩的一个坑,背景是这样的:我们的项目依赖于一个外部服务&#x…

解决exe文件在别人电脑上运行缺失文件情况

这里就以vs2013为例:编译后生成的exe文件拷贝到别人电脑上运行是会弹出一个窗口说缺失MSVCR120.dll和MSVCR120D.dll这两个文件。(其他vs版本的编译器在所提示的缺失文件按下述方法也可解决)下面就介绍一种方法解决。 1、在VS2013软件中找到MS…

32张图带你彻底搞懂事务和锁!

作者 | 悟空聊架构来源 | 悟空聊架构(ID:PassJava666)转载请联系授权(微信ID:PassJava)本篇主要内容如下:本篇主要内容一、事务1.1 什么是事务为单个工作单元而执行的一系列操作。如查询、修改数…

分布式映射与集中式映射_K映射上的表达式映射和组包围

分布式映射与集中式映射In the previous article (Karnaugh Map 2, 3 and 4- variable) we have already discussed the designing of K-Map and various forms in which they are represented based on either they are being mapped for minterm or maxterm. 在上一篇文章( 卡…

JDK 竟然是这样实现栈的?

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)前面的文章《动图演示:手撸堆栈的两种实现方法!》我们用数组和链表来实现了自定义的栈结构&#xff…

关于微信,运营商们就这点志向?

2019独角兽企业重金招聘Python工程师标准>>> 近期关于运营商威逼微信收费之事闹得沸沸扬扬,在虎嗅上看到有不少人发表了自己的看法也不乏给运营商或微信出点子的人,但我觉得都不是很妥,还是谈谈我的看法吧。 陈旧的思路&#xff…

阿里巴巴开源的Excel操作神器!

前提导出数据到Excel是非常常见的后端需求之一,今天来推荐一款阿里出品的Excel操作神器:EasyExcel。EasyExcel从其依赖树来看是对apache-poi的封装,笔者从开始接触Excel处理就选用了EasyExcel,避免了广泛流传的apache-poi导致的内…

再谈指针

C语言为什么高效?因为C语言有指针。指针是C语言的精华,同时也是C语言的难点,很多人一学到指针就表示头大,指针的指向往往把人搞得晕头转向,甚至有的人为了避免使用指针居然不惜多写几十行代码,无疑增加了工…

Word 2003中为什么修改一个段落的文章结果整篇文档的格式都变?

问题比如说,我选定某一段把颜色改成***,结果整篇文档都变成***了,按撤退健,才能达到效果(只有这段变成***,其他的不变)。答案打开格式菜单中的[样式和格式],找到样式中的“正文”。 …

链表反转的两种实现方法,后一种击败了100%的用户!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)链表反转是一道很基础但又非常热门的算法面试题,它也在《剑指Offer》的第 24 道题出现过,至于它有多…

squid代理服务器(捎带的SNAT)

1.传统代理传统代理可以隐藏IP地址 多用于Internet 在Linux中 默认没有安装squid 所以要安装 在red hat中 还要安装perl 语言包的支持 squid代理服务器需要两块网卡 首先保证你的流量是从linux服务器上过的 所以先保证做完SNAT可以互相通信1)配置网络参数在试验中一…

MySQL开源工具推荐,有了它我卸了珍藏多年Nactive!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)最近无意间发现了一款开源免费的 MySQL 客户端管理工具,磊哥试用了两天感觉还行,所以今天推荐给各位…

memoryTraining记忆训练小游戏

无聊的时候用C写了一个记忆训练的小游戏、、、 灵感源于一个flash的小游戏学到C语言就用C语言实验了一下,做出来。好久以前的东西了,数组用的还不咋样,现在看看把数组下标0漏掉了、、、掉了修补了修补,先扔这儿吧。源码下载

动态调用动态库方法 .so

2019独角兽企业重金招聘Python工程师标准>>> 关于动态调用动态库方法说明 一、 动态库概述 1、 动态库的概念 日常编程中,常有一些函数不需要进行编译或者可以在多个文件中使用(如数据库输入/输 出操作或屏幕控制等标准任务函数&#…

算法图解:如何找出栈中的最小值?

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)前面我们学习了很多关于栈的知识,比如《动图演示:手撸堆栈的两种实现方法!》和《JDK 竟然…