GitHub 17k Star 的Java工程师成神之路,不来了解一下吗!

GitHub 17k Star 的Java工程师成神之路,真的不来了解一下吗!

GitHub 17k Star 的Java工程师成神之路,真的真的不来了解一下吗!

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

很多人都知道,在进行金额表示、金额计算等场景,不能使用double、float等类型,而是要使用对精度支持的更好的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));

以上代码,输出结果为:

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

那么,为什么精度不同呢?为什么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方法。

关注公众号:程序员面试现场。回复『手册』获取手册最新版。

本文由博客一文多发平台 OpenWrite 发布!

为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?的更多相关文章

  1. List中的Contains方法内部其实是用对象的equals方法做比较,所以如果比较两个类就重写类的equals方法即可;而Set是调用equals和hashCode

    public class Person { private String name; private int age; public String getName() { return name; } ...

  2. Bigdecimal 比较equals与compareTo

    原文链接:https://blog.csdn.net/jixinhuluwa/article/details/72626598 1.b.equals(BigDecimal.ZERO); 该方法存在的问 ...

  3. Java中BigDecimal的equals与compareTo的区别

          有个是否为零的判断[BigDecimal.ZERO.equals(ratio)]我用了BigDecimal的equals方法,结果,判断失败,因此特地分析一下equals与compareT ...

  4. JAVA中的各种 哈希码(HashCode) 与 equals方法在HIBERNATE的实际应用[转载]

    1.什么是哈希码(HashCode) 在Java中,哈希码代表对象的特征.例如对象 Java代码 String str1 = “aa”, str1.hashCode= 3104 String str2 ...

  5. java为什么要重写hashCode和equals方法?

    如果不被重写(原生)的hashCode和equals是什么样的? 不被重写(原生)的hashCode值是根据内存地址换算出来的一个值. 不被重写(原生)的equals方法是严格判断一个对象是否相等的方 ...

  6. 重写hashcode和equals方法

    重写hashcode和equals方法 简乐君 2019-05-07 21:55:43 35481 收藏 191分类专栏: Java 文章标签: equals() hashcode()版权 一.前言我 ...

  7. 为什么阿里巴巴Java开发手册中强制要求整型包装类对象值用 equals 方法比较?

    在阅读<阿里巴巴Java开发手册>时,发现有一条关于整型包装类对象之间值比较的规约,具体内容如下: 这条建议非常值得大家关注, 而且该问题在 Java 面试中十分常见. 还需要思考以下几个 ...

  8. 所有整数型包装类对象值的比较,使用equals方法进行比较

    一.整数型包装类对象值的比较,使用equals方法进行比较 题眼:整型包装类.值的比较 注:== :对于基本类型,比较的是值:对于引用类型,比较的是地址值. // 组一Integer i1=new I ...

  9. 关于Java中的equals方法

    关于Java中的equals方法 欢迎转载,但是请填写本人的博客园原址https://www.cnblogs.com/JNovice/p/9347099.html 一.什么是equals方法 equa ...

随机推荐

  1. 10.oracle分页

    oracle的分页一共有三种方式 方法一 根据rowid来分 SELECT * FROM EMP WHERE ROWID IN (SELECT RID FROM (SELECT ROWNUM RN, ...

  2. 高德地图marker事件监听-高德地图marker绑定事件就执行了[解决立即执行]

    官方的demo是这样的:地址:[http://lbs.amap.com/api/javascript-api/example/infowindow/add-infowindows-to-multipl ...

  3. 使用vim的妙招

    使用F1执行文件 Vim是一个类似于Vi的著名的功能强大.高度可定制的文本编辑器. 我们Linux运维经常在Linux中使用到Vim编辑器,当使用Vim写shell脚本或者python脚本的时候,想要 ...

  4. ACM集训第一次积分赛赛前复习+day4

    不知不觉4天过去了,我们迎来了我们第一次积分赛,赛前的四天我们学了以下知识点吧: day 1.排序 之前一直想用qsort,但是总是写不明白,STL的sort()可以说是很方便了. 先写一个最基础的数 ...

  5. Java多线程_Atomic

    1.什么是Atomic?Atomic,中文意思是“原子的”,在java多线程中,有这样的一个包: java.util.concurrent.atomic——线程安全的原子操作包 这是JDK1.5的版本 ...

  6. 记录一次CDH集群邮件报警功能的设置

    1.通用的配置CDH邮件报警设置 进入cloudera manager service页面,选择配置 左侧菜单Alert Publisher 勾选[启用电子邮件警报] 邮件服务协议smtp,如果使用s ...

  7. 开源搜索引擎排名第一,Elasticsearch是如何做到的?

    一.引言 随着移动互联网.物联网.云计算等信息技术蓬勃发展,数据量呈爆炸式增长.如今我们可以轻易得从海量数据里找到想要的信息,离不开搜索引擎技术的帮助. ​ 作为开源搜索引擎领域排名第一的 Elast ...

  8. e3mall商城的归纳总结5之修改商品分类、e3mall—content的搭建

    说在前面的话 本节基本上没有用到新的知识点.主要还是对数据库的增删改查以及创建了一个新的内容模块. 新增商品分类 由于easyUI的Tree需要三个字段(Id.state.text), [{ &quo ...

  9. MD5加密,java String 转变成MD5 String 详细代码,工具类Android开发必备

    /** * MD5加码.32位 * @param inStr * @return */ public static String MD5(String inStr) { MessageDigest m ...

  10. Mysql性能参数优化

    1.Max_connections (1)简介 Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,当然这是要建立在机器能够支撑的情况下,因为如果连接数越来越多,mysql会为每个连 ...