大厂面试系列-为什么Java中的BigDecimal不能用equals来比较大小?

大厂面试系列-为什么Java中的BigDecimal不能用equals来比较大小?在很多涉及到与资金相关的计算中,因为使用了Dubbo或者使用Float都会导致精度损失等问题的出现,所以为了计算简单在有些场景中会直接将金额采用

大家好,欢迎来到IT知识分享网。

大厂面试系列-为什么Java中的BigDecimal不能用equals来比较大小?

BigDecimal 是Java中提供用来进行高精度计算的数据类型。在很多涉及到与资金相关的计算中,因为使用了Dubbo或者使用Float都会导致精度损失等问题的出现,所以为了计算简单在有些场景中会直接将金额采用“分”这个单位来进行计算,这样就不会出现精度损失的问题,但这只是在特定场景中在许多高精度的科学计算中BigDecimal却发挥这重要的作用,当然在支付相关的行业也离不开BigDecimal的高精度计算。

BigDecimal类自身就提供了一些加减乘除的运算,当然除了这些基础运算之外,它还提供了一些判断数字相等的操作。下面我们就来一起探索一下BigDecimal中对于数字相等的判断。

两数相等的判断

在我们日常的开发中我们判断两个数的相等直接使用==的方式就可以判断,代码如下

public class Test { public static void main(String[] args) { int a = 1; int b = 2; System.out.println(a==b); } }

这个是我们日常开发中的对于两个普通数据的判断,下面我们来看一下如果对于两个BigDecimal的数据如何进行相等判断。

public class Test { public static void main(String[] args) { BigDecimal a = new BigDecimal(1231); BigDecimal b = new BigDecimal(); System.out.println(a==b); } }

会看到我们如果使用了==的方式进行判断显然根据之前我们对于对象的分析,它判断的其实是两个对象的引用是否是一致的。很显然,两个数据都是采用new的方式进行创建的,所以说输出的结果一定是false。

那么既然不能用==的方式来判断,我们是否可以使用equals()方法进行判断。代码如下

public class Test { public static void main(String[] args) { BigDecimal a = new BigDecimal(1231); BigDecimal b = new BigDecimal(1231); System.out.println(a.equals(b)); BigDecimal c = new BigDecimal(1231); BigDecimal d = new BigDecimal(1231.0); System.out.println(c.equals(d)); BigDecimal e = new BigDecimal("1231"); BigDecimal f = new BigDecimal("1231.0"); System.out.println(e.equals(f)); } }

运行代码会看到前两个结果为true,而第三个结果为false。这是为什么呢?下面我们就来分析一下BigDecimal的equals()方法。

BigDecimal的equals()方法

根据上面代码运行的结果来看,在我们使用了BigDecimal 的equal()方法对1231与1231.0进行比较的时候,返回结果为ture,而使用字符串进行比较的时候就返回为false。这是什么原因造成呢?我们先来看一下BigDecimal重写的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()方法与compareTo()方法的比较方式是不一样的equals()方法在比较value的同时还会比较一个scale值。

所以,实际上我们在比较e、f对象的时候由于scale的值是不一样的,所以得到equal()的值就是false。而对于a、b、c、d来讲他们都是对于在我们理解的范围内的数字比较。

那么这里就会有人问了在我们比较 c、d两个数据的时候分明一个是1231,一个是1231.0两个数字的scale值为什么会一样呢?

为什么scale值会不一样?

我们先来看一下源码,在BigDecimal 的源码中有如下的一段代码。

 /** * The scale of this BigDecimal, as returned by {@link #scale}. * * @serial * @see #scale */ private final int scale; // Note: this may have any value, so // calculations must be done in longs

从这段代码中我们可以知道,这个就是一个标度值,用来标识数据。

而在BigDecimal的构造方法中都对scale值相应的设置。由于方法内容过长,这里不粘贴源码进行讲解,有兴趣的读者可以查看响应的源码。

说了这么多,那么我们到底应该如何比较两个BigDecimal两个数据的相等呢?

比较BigDecimal 相等的正解

在上面的介绍中我们介绍了BigDecimal中equals()方法并不能用来判断BigDecimal对象相等,因为会涉及到标度的比较。

那么在BigDecimal中如何比较两个数据是否相等呢?这个时候就要用到compareTo()方法,下面我们先来看一下这个方法的源码

/** * Compares this {@code BigDecimal} with the specified * {@code BigDecimal}. Two {@code BigDecimal} objects that are * equal in value but have a different scale (like 2.0 and 2.00) * are considered equal by this method. This method is provided * in preference to individual methods for each of the six boolean * comparison operators ({@literal <}, ==, * {@literal >}, {@literal >=}, !=, {@literal <=}). The * suggested idiom for performing these comparisons is: * {@code (x.compareTo(y)} <<i>op</i>> {@code 0)}, where * <<i>op</i>> is one of the six comparison operators. * * @param val {@code BigDecimal} to which this {@code BigDecimal} is * to be compared. * @return -1, 0, or 1 as this {@code BigDecimal} is numerically * less than, equal to, or greater than {@code val}. */ public int compareTo(BigDecimal val) { // Quick path for equal scale and non-inflated case. if (scale == val.scale) { long xs = intCompact; long ys = val.intCompact; if (xs != INFLATED && ys != INFLATED) return xs != ys ? ((xs > ys) ? 1 : -1) : 0; } int xsign = this.signum(); int ysign = val.signum(); if (xsign != ysign) return (xsign > ysign) ? 1 : -1; if (xsign == 0) return 0; int cmp = compareMagnitude(val); return (xsign > 0) ? cmp : -cmp; }

源码中我们看看到,其比较原理,就是直接对两个数据进行了数值比较。如果两个数据表示的数值是一样的,则返回0。我们可以将上面的测试代码改成如下的样子。

public class Test { public static void main(String[] args) { BigDecimal a = new BigDecimal(1231); BigDecimal b = new BigDecimal(1231); System.out.println(a.compareTo(b)); BigDecimal c = new BigDecimal(1231); BigDecimal d = new BigDecimal(1231.0); System.out.println(c.compareTo(d)); BigDecimal e = new BigDecimal("1231"); BigDecimal f = new BigDecimal("1231.0"); System.out.println(e.compareTo(f)); } }

从返回结果中可以看到,三个结果都是0,也就是说三个数字的数值比较都是一样的。

总结

BigDecimal是Java提供的一个用于高精度计算的类,并且其中提供了各种支持高精度计算的方法。但是在对于BigDecimal高精度运算的时候一定要注意其使用的方式。尤其是在做一些数值比较或者数值运算的时候,一定要注意使用正确的用法。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/55316.html

(0)
上一篇 2024-07-26 21:33
下一篇 2024-07-27 11:15

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信