都是 0,为什么 BigDecimal.ZERO 和 0.00 比较竟然是 false?



那天早上,我照例去公司楼下买咖啡。我掏出手机,对老板说:"老板,我钱包里还有 0 块钱,能不能先赊一杯?"

老板瞄了一眼我的余额页面:

  • 页面一:余额:0
  • 页面二:余额:0.00

老板一脸严肃:"小伙子,你这两个不一样。",我当场愣住。"0 不就是 0 吗?你这不是数学问题,是态度问题吧?"

老板慢悠悠地说了一句让我后来在代码里反复咀嚼的话: "数值一样,不代表记账方式一样。"

那一刻,我仿佛听见了 BigDecimal 在我耳边冷笑。

问题重现:一行代码引发的血案

我们先来看那段"罪魁祸首"代码。

输出结果是:

是不是很反直觉?

"都是 0 啊兄弟,你俩怎么就不能好好相处?"

别急,Java 没疯,是我们低估了 BigDecimal。

BigDecimal 到底在"较真"什么?

BigDecimal 不只是"值",还有"刻度"

BigDecimal 内部,其实由两部分组成:

  • unscaledValue(未缩放的整数值)
  • scale(小数位数)

我们拆开来看:

等价于:

而:

等价于:

数值相同,但小数位精度不同。这就好比:

  • 0:口袋里没钱
  • 0.00:钱包里有两位小数精度的余额,只是刚好是零

在财务系统眼里,这俩可不是一回事。

equals 为啥这么"死板"?

我们直接看源码(简化版)。

划重点:BigDecimal 的 equals,既比较值,也比较 scale。

所以:

  • 0(scale=0)
  • 0.00(scale=2)
  • scale 不同,equals = false

Java 在这里的设计理念只有一句话: "你既然用 equals,那我就精确到每一位给你算。"

Objects.equals 并没有"背锅"

很多同学第一反应是:"是不是 Objects.equals 有问题?"

其实完全不是。

Objects.equals 本质上只是:

  • 帮你处理 null
  • 最终还是调用 a.equals(b)

所以真正的"裁判",从头到尾都是 BigDecimal 的 equals。

一个更扎心的对照实验

我们来做一组对比。

但如果换一种方式:

是不是瞬间清醒了?

equals vs compareTo,本质差异在哪?

我给你整理了一张"面试必背级别"的表。

一句话总结:equals 是"格式敏感型选手",compareTo 是"结果导向型选手"。

生活类比:记账本 vs 心算

把 BigDecimal 想成两种人:

  • equals:会计
  • "你给我的是 0 还是 0.00?凭证不一样,我就不认。"
  • compareTo:老板
  • "别跟我扯格式,你就说是不是零。"

业务判断 里,你通常更像老板;在数据结构(Set / Map) 里,Java 更像会计。

HashMap 里的"隐形大坑"

如果你用 BigDecimal 作为 Map 的 key:

结果是:

这意味着什么?在 HashMap 眼里,0 和 0.00 是两个完全不同的 key。

如果这是:

  • 金额缓存
  • 财务统计
  • 对账系统

那基本就是线上事故预备役

正确姿势一:统一 scale

在进入 Map / Set 之前,先"洗一遍数据"。

示例:

正确姿势二:业务判断永远用 compareTo

这是我在代码评审里见一次夸一次的写法。

面试官最爱问的追问

Q:那为什么 BigDecimal 不在 equals 里忽略 scale?

这是个设计哲学问题。原因有三点:

  1. 精度本身就是信息
  2. 财务、统计、科学计算场景必须严谨
  3. equals 一旦改规则,会破坏 HashMap、HashSet 的一致性

Java 的选择是: "我不替你做业务判断,我只保证对象语义一致。"

终极对照表(建议收藏)

一句话总结(可以直接背)

BigDecimal 的 equals 比的是"值 + 精度",compareTo 比的是"数值大小",业务判断永远优先用 compareTo。

END

写到这里,我又想起那家咖啡店老板。在生活里:0 就是没钱。但在代码里:0 是 0,0.00 是 0.00,你要说清楚你想比什么。

BigDecimal 从来不坑你,它只是比你想得更认真一点。

如果你觉得这篇文章帮你少踩了一个坑,欢迎点个 "在看"、转发给你的好朋友。

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!

相关推荐
不知疲倦的仄仄12 小时前
第一天:从 ByteBuffer 内存模型到网络粘包处理实战
java·网络·nio
Tinachen8812 小时前
YonBIP旗舰版本地开发环境搭建教程
java·开发语言·oracle·eclipse·前端框架
星火开发设计12 小时前
堆排序原理与C++实现详解
java·数据结构·c++·学习·算法·排序算法
七七powerful12 小时前
docker28.1.1和docker-compose v.2.35.1安装
java·docker·eureka
言之。12 小时前
DDIA第四章 数据库存储引擎面试问题集
数据库·面试·职场和发展·ddia
努力学算法的蒟蒻12 小时前
day55(1.6)——leetcode面试经典150
算法·leetcode·面试
小白学大数据12 小时前
百科词条结构化抓取:Java 正则表达式与 XPath 解析对比
java·开发语言·爬虫·正则表达式
2501_9418072612 小时前
在迪拜智能机场场景中构建行李实时调度与高并发航班数据分析平台的工程设计实践经验分享
java·前端·数据库
加洛斯12 小时前
SpringSecurity入门篇(1)
后端·架构
一 乐12 小时前
餐厅点餐|基于springboot + vue餐厅点餐系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端