那天早上,我照例去公司樓下買咖啡。我掏出手機,對老闆説:“老闆,我錢包裏還有 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?
這是個設計哲學問題。原因有三點:
- 精度本身就是信息
- 財務、統計、科學計算場景必須嚴謹
- equals 一旦改規則,會破壞 HashMap、HashSet 的一致性
Java 的選擇是:“我不替你做業務判斷,我只保證對象語義一致。”
終極對照表(建議收藏)
一句話總結(可以直接背)
BigDecimal 的 equals 比的是“值 + 精度”,compareTo 比的是“數值大小”,業務判斷永遠優先用 compareTo。
END
寫到這裏,我又想起那家咖啡店老闆。在生活裏:0 就是沒錢。但在代碼裏:0 是 0,0.00 是 0.00,你要説清楚你想比什麼。
BigDecimal 從來不坑你,它只是比你想得更認真一點。
如果你覺得這篇文章幫你少踩了一個坑,歡迎點個“在看”、轉發給你的好朋友。
我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!