Definition
- **equals()**: checks both numeric value AND scale; `new BigDecimal("1.0")` (scale 1) is NOT equal to `new BigDecimal("1.00")` (scale 2)
- **compareTo()**: checks numeric value only; `1.0.compareTo(1.00) == 0` (equal)
- **hashCode()**: consistent with equals, so 1.0 and 1.00 have DIFFERENT hash codes
- **TreeMap/TreeSet**: use compareTo → treat 1.0 and 1.00 as the same key (correct for finance)
- **HashMap/HashSet**: use equals → treat 1.0 and 1.00 as different keys (dangerous for finance)
- **Fix**: use `stripTrailingZeros()` to normalize scale, or use TreeMap for BigDecimal keysIn banking, 50000.0 rupees and 50000.00 rupees are the same amount of money. The compareTo method agrees – they are equal. But the equals method says they are different because one has one decimal place and the other has two. Using equals to compare financial amounts is like saying "50000.0 rupees is not the same as 50000.00 rupees" – technically different notation, but the same value.
Understanding the Concepts
Why does BigDecimal compareTo differ from equals?
BigDecimal internally stores a value and a scale (number of decimal places). equals() considers both; compareTo() considers only the numeric value.
package com.bank.demo;
import java.math.BigDecimal;
public class BigDecimalComparisonDemo {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("1.0"); // value=10, scale=1
BigDecimal b = new BigDecimal("1.00"); // value=100, scale=2
System.out.println("equals: " + a.equals(b)); // false!
System.out.println("compareTo: " + a.compareTo(b)); // 0 (equal)
// hashCode is consistent with equals, so different:
System.out.println("hashCode a: " + a.hashCode()); // different
System.out.println("hashCode b: " + b.hashCode()); // different
// In finance, 1.0 and 1.00 are the SAME amount!
// Always use compareTo for financial comparisons:
if (a.compareTo(b) == 0) {
System.out.println("Same amount (correct for banking)");
}
}
}
equals: false
compareTo: 0
hashCode a: 311
hashCode b: 3101
Same amount (correct for banking)
Why does this matter for collections?
HashMap and HashSet use equals() and hashCode() for key comparison. TreeMap and TreeSet use compareTo(). This means the same BigDecimal values behave differently depending on which collection you use.
package com.bank.demo;
import java.math.BigDecimal;
import java.util.*;
public class BigDecimalCollectionDemo {
public static void main(String[] args) {
BigDecimal key1 = new BigDecimal("50000.0");
BigDecimal key2 = new BigDecimal("50000.00");
// HashMap: uses equals → different keys!
Map<BigDecimal, String> hashMap = new HashMap<>();
hashMap.put(key1, "Rajesh");
hashMap.put(key2, "Priya");
System.out.println("HashMap size: " + hashMap.size()); // 2! Both stored!
System.out.println("HashMap get(1.0): " + hashMap.get(key1)); // Rajesh
System.out.println("HashMap get(1.00): " + hashMap.get(key2)); // Priya
// TreeMap: uses compareTo → same key!
Map<BigDecimal, String> treeMap = new TreeMap<>();
treeMap.put(key1, "Rajesh");
treeMap.put(key2, "Priya"); // overwrites! compareTo == 0
System.out.println("\nTreeMap size: " + treeMap.size()); // 1
System.out.println("TreeMap get: " + treeMap.get(key1)); // Priya
// HashSet: uses equals → different elements!
Set<BigDecimal> hashSet = new HashSet<>();
hashSet.add(key1);
hashSet.add(key2);
System.out.println("\nHashSet size: " + hashSet.size()); // 2!
// TreeSet: uses compareTo → same element!
Set<BigDecimal> treeSet = new TreeSet<>();
treeSet.add(key1);
treeSet.add(key2);
System.out.println("TreeSet size: " + treeSet.size()); // 1
}
}
HashMap size: 2
HashMap get(1.0): Rajesh
HashMap get(1.00): Priya
TreeMap size: 1
TreeMap get: Priya
HashSet size: 2
TreeSet size: 1
How do you fix this for HashMap?
Normalize the scale using stripTrailingZeros() before using BigDecimal as a key:
package com.bank.demo;
import java.math.BigDecimal;
import java.util.*;
public class BigDecimalNormalizeDemo {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("50000.0").stripTrailingZeros();
BigDecimal b = new BigDecimal("50000.00").stripTrailingZeros();
System.out.println("equals after strip: " + a.equals(b)); // true!
System.out.println("hashCode match: " + (a.hashCode() == b.hashCode())); // true!
Map<BigDecimal, String> map = new HashMap<>();
map.put(a, "Rajesh");
map.put(b, "Priya"); // overwrites – same key after normalization
System.out.println("HashMap size: " + map.size()); // 1
}
}
equals after strip: true
hashCode match: true
HashMap size: 1
What are common mistakes?
Mistake 1: Using equals for financial comparison
// WRONG: equals considers scale
if (balance.equals(new BigDecimal("0"))) { } // fails for 0.00, 0.0, 0.000
// CORRECT: compareTo ignores scale
if (balance.compareTo(BigDecimal.ZERO) == 0) { } // works for all scales
Mistake 2: Using BigDecimal as HashMap key without normalization
// WRONG: 50000.0 and 50000.00 are different HashMap keys
Map<BigDecimal, String> map = new HashMap<>();
map.put(new BigDecimal("50000.0"), "A");
map.put(new BigDecimal("50000.00"), "B");
// map.size() = 2! Duplicate amounts stored!
// CORRECT: normalize or use TreeMap
Map<BigDecimal, String> treeMap = new TreeMap<>(); // uses compareTo
What should a senior engineer know?
- This is a violation of the Comparable contract. The Comparable documentation says that compareTo should be consistent with equals: a.compareTo(b) == 0 should imply a.equals(b). BigDecimal violates this intentionally for mathematical correctness (1.0 and 1.00 have the same value but different precision).
- Use TreeMap for BigDecimal keys in banking. TreeMap uses compareTo, which correctly treats 50000.0 and 50000.00 as the same key. HashMap would create two entries for the same amount.
- BigDecimal.ZERO has scale 0. new BigDecimal("0.00") has scale 2. They are not equals, but compareTo returns 0.
What are common interview follow-up questions?
- "Are 1.0 and 1.00 equal in BigDecimal?" – equals: no; compareTo: yes.
- "Which collection should you use for BigDecimal keys?" – TreeMap (uses compareTo).
- "How do you fix HashMap with BigDecimal keys?" – stripTrailingZeros() before put.
- "Why does BigDecimal violate the Comparable contract?" – Mathematical precision (1.0 has 1 decimal place, 1.00 has 2).
Visual Overview
BigDecimal: equals vs compareTo
new BigDecimal("1.0") → value=10, scale=1
new BigDecimal("1.00") → value=100, scale=2
equals(): value AND scale → 10/1 ≠ 100/2 → FALSE
compareTo(): numeric value → 1.0 == 1.00 → 0 (EQUAL)
HashMap (uses equals): 1.0 and 1.00 = DIFFERENT keys (bug!)
TreeMap (uses compareTo): 1.0 and 1.00 = SAME key (correct)
Fix for HashMap:
stripTrailingZeros(): 1.0 → 1, 1.00 → 1 → equals = true
Key Takeaway
BigDecimal's equals method checks both value and scale, so 1.0 and 1.00 are not equal. The compareTo method checks only the numeric value, so 1.0 and 1.00 are equal. In financial applications, this distinction matters because the same amount of money can arrive with different decimal scales.
The practical impact hits collections. HashMap uses equals, so 50000.0 and 50000.00 are stored as separate entries – a subtle duplicate that corrupts balance lookups. TreeMap uses compareTo, correctly treating them as the same key.
In the National Bank of India system, Rajesh always uses compareTo for BigDecimal comparisons and TreeMap for BigDecimal-keyed maps. When HashMap is required for performance, he normalizes keys with stripTrailingZeros() before insertion.
Remember one sentence: BigDecimal equals checks scale (1.0 is not 1.00), compareTo checks value (1.0 equals 1.00) – in finance, always use compareTo and prefer TreeMap for BigDecimal keys.
---