为什么不能用 BigDecimal 的 equals 方法做等值比较?
为什么不能用 BigDecimal 的 equals 方法做等值比较?
zhangchangsheng面试考察点
- API 理解深度:面试官不仅仅是想知道 “不能用”,更是想考察你是否读过
BigDecimal的源码,理解equals()和compareTo()的实现差异。 - 精度意识:
BigDecimal的核心特性是精度可控,这个问题考察你是否理解scale(标度)的概念及其对比较的影响。 - 实战经验:金额比较是金融场景的高频操作,用错方法会导致严重的业务 bug,这反映你的工程经验。
核心答案
BigDecimal 的 equals() 方法不仅比较数值,还比较精度,导致 1.0 和 1.00 被判定为不相等。比较 BigDecimal 的数值应该使用 compareTo() 方法。 ****
| 比较方法 | 比较内容 | 1.0 vs 1.00 结果 | 推荐场景 |
|---|---|---|---|
| equals() | 数值 + 精度 | false(不相等) | ❌ 不推荐用于数值比较 |
| compareTo() | 仅数值 | 0(相等) | ✅ 数值比较首选 |
| compareTo() == 0 | 仅数值 | true(相等) | ✅ 推荐写法 |
一句话总结:equals() 看精度,compareTo() 看数值,金额比较用 compareTo()。
深度解析
一、翻车现场:equals 的 “坑”
import java.math.BigDecimal; |
看到了吗?1.0 和 1.00 在数学上明明相等,但 equals() 返回 false!这在金额比较场景中是致命的 bug。
二、为什么会这样?scale 的概念
BigDecimal = unscaledValue(整数部分) × 10^(-scale),scale 即小数位数。
| 对比项 | new BigDecimal(“1.0”) | new BigDecimal(“1.00”) |
|---|---|---|
| unscaledValue(整数部分) | 10 | 100 |
| scale(小数位数) | 1 | 2 |
| 实际值 | 10 × 10^(-1) = 1.0 | 100 × 10^(-2) = 1.00 |
| equals() 结果 | false(unscaledValue 与 scale 均不同) | false |
虽然数学值相同(都是 1),但内部存储不同。equals() 的比较规则是 unscaledValue 和 scale 都必须相等,因此 1.0 与 1.00 用 equals() 比较返回 false。
上图展示了 BigDecimal 的内部结构和 equals() 的比较逻辑。关键要点:
scale(标度):小数点后的位数,1.0的 scale 是 1,1.00的 scale 是 2equals()严格比较:不仅要数值相等,scale也必须相等compareTo()宽松比较:只比较数学值,忽略scale差异
三、源码分析
// BigDecimal.equals() 源码(简化版) |
四、实际场景对比
import java.math.BigDecimal; |
五、最佳实践总结
| 场景 | 推荐方法 | 示例 |
|---|---|---|
| 判断相等 | compareTo() == 0 | a.compareTo(b) == 0 |
| 判断大于 | compareTo() > 0 | a.compareTo(b) > 0 |
| 判断小于 | compareTo() < 0 | a.compareTo(b) < 0 |
| 判断大于等于 | compareTo() >= 0 | a.compareTo(b) >= 0 |
| 判断小于等于 | compareTo() <= 0 | a.compareTo(b) <= 0 |
| 排序/TreeSet | compareTo() | 自动使用 |
面试高频追问
BigDecimal有哪些构造方式?推荐哪种? ****构造方式 示例 精度问题 推荐 字符串构造 new BigDecimal(“0.1”) ✅ 精确 ⭐ 推荐 double 构造 new BigDecimal(0.1) ❌ 精度丢失 ❌ 禁止 valueOf BigDecimal.valueOf(0.1) ✅ 精确 ⭐ 推荐 TreeSet中放BigDecimal会有问题吗? 不会有问题。TreeSet使用compareTo()排序,1.0和1.00会被视为相同元素(只能存一个)。但如果用HashSet,由于equals()不同,两个都会存进去。如何统一
BigDecimal的精度?
BigDecimal a = new BigDecimal("1.0"); |
常见面试变体
- 变体一:”
BigDecimal的compareTo()和equals()有什么区别?” - 变体二:”为什么
new BigDecimal(0.1)得到的不是精确的 0.1?” - 变体三:”
BigDecimal如何比较大小?”
记忆口诀
equals 比精度,1.0 不等 1.00; compareTo 比数值,金额比较它靠谱; 构造用字符串,double 构造坑死人。
总结
BigDecimal 的 equals() 方法会比较 scale(精度),导致 1.0 和 1.00 被判定为不相等。金额比较必须使用 compareTo() == 0,只比较数值大小,忽略精度差异。同时,构造 BigDecimal 时应使用字符串或 valueOf(),避免 double 构造导致的精度丢失。
声明:本文转载自小哈的博客 《为什么不能用 BigDecimal 的 equals 方法做等值比较?》,版权归原文作者所有。转载请保留本声明与原文链接。
