BigDecimal 的 compareTo 和 equals 不一致是文档里明确警告过的"陷阱",这篇真实用同一批数据分别喂给 HashSet 和 TreeSet,验证这句警告到底会造成多大的真实分歧。
上一篇:线程未捕获异常处理器真实验证
系列前面验证过BigDecimal("1.0").equals("1.00")为false——这次专门验证compareTo()给出的真实是相反答案。
CompareToEqualsInconsistencyDemo.java BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("1.00"); a.equals(b); a.compareTo(b);
a.equals(b) 真实结果: false(equals()真实认为scale不同就不相等) a.compareTo(b) 真实结果: 0(compareTo()真实只比较数值,认为相等)
真实结果印证了 BigDecimal 官方文档明确写出的警告:equals() 把小数位数(scale)也纳入判等标准,"1.0" 和 "1.00" 因为 scale 不同(分别是1和2)真实被认为是不同的对象;而 compareTo() 遵循的是数学意义上的数值比较,两者数值相同,真实返回 0(相等)。这是 JDK 里少数几个明确"违反" Comparable 接口文档建议("compareTo的结果最好和equals保持一致")的类之一。
HashSet依赖equals/hashCode判等,TreeSet依赖compareTo判等——用同一批含"重复值"的数据分别喂给两者。
CompareToEqualsInconsistencyDemo.java List<BigDecimal> values = List.of( new BigDecimal("2.0"), new BigDecimal("2.00"), new BigDecimal("2.000"), new BigDecimal("3.0") ); new HashSet<>(values); new TreeSet<>(values);
同一批4个值(含3个数值相同但scale不同),HashSet去重后真实size: 4 同一批4个值,TreeSet去重后真实size: 2(真实把2.0/2.00/2.000当成同一个值)
同样的判等标准差异,在TreeMap/HashMap上会如何体现。
treeMap.containsKey(b) 真实结果: true(TreeMap真实用compareTo判断key是否存在,b和a数值相同) treeMap.get(b) 真实结果: 存的是a对应的值(真实能用b查到a存的值) hashMap.containsKey(b) 真实结果: false(HashMap真实用equals判断,b和a的equals()为false)
真实结果再次印证了同一套原理:用 a("1.0")存入的值,TreeMap 真实允许用数值相同但 scale 不同的 b("1.00")查到,而 HashMap 真实查不到——两种 Map 实现对"key是否相同"这个基本问题,因为底层依赖的判等接口不同,真实给出了完全相反的答案。这也是官方文档给出的明确建议:涉及 BigDecimal 的场景,如果要用作集合的元素或 key,需要提前想清楚业务上到底需要"数值相等"(用 TreeSet/TreeMap,或者先用 stripTrailingZeros() 统一 scale 再用 HashSet/HashMap)还是"完全一致"(HashSet/HashMap 默认语义)。
每一步的 size 和内容都是真实运行这段代码得到的,不是按规则手工推的。