This seems a lot like an X-Y problem; as pointed out in the comments it doesn't make a lot of sense to be comparing arbitrary Number
objects; why not just make your maps Map<BigDecimal, String>
and just always use BigDecimal
directly (normalized with stripTrailingZeros()
if necessary)? Then you can just use standard Map
equality. Consider stepping back and asking whether you really need the behavior you're describing in the first place.
Notably, Number
doesn't implement Comparable
, which should be a pretty clear hint that you shouldn't try to compare arbitrary Number
instances.
If that isn't an option for some reason, here's a reasonable implementation of the behavior you're describing.
private static BigDecimal toBigDecimal(Number n) {
return n instanceof BigDecimal ? (BigDecimal)n : new BigDecimal(n.toString());
}
public static <V> boolean numberMapEquality(
Map<Number, ? extends V> a, Map<Number, ? extends V> b) {
if (a == b) return true;
if (a.size() != b.size()) return false;
// TreeMap uses .compareTo(), not .equals()
TreeMap<BigDecimal, V> bdMap = new TreeMap<>();
for (Entry<Number, ? extends V> e : a.entrySet()) {
bdMap.put(toBigDecimal(e.getKey()), e.getValue());
}
if (bdMap.size() != a.size()) {
// comment out if you don't care about this edge-case - but you should
throw new IllegalArgumentException(
"Multiple keys in 'a' normalize to the same value; " +
"equality comparison is unsafe.");
}
// Taken from AbstractMap.equals()
for (Entry<Number, ? extends V> e : b.entrySet()) {
BigDecimal bdKey = toBigDecimal(e.getKey());
V value = e.getValue();
if (value == null) {
if (!(bdMap.get(bdKey)==null && bdMap.containsKey(bdKey)))
return false;
} else {
if (!value.equals(bdMap.get(bdKey)))
return false;
}
}
return true;
}
Alternatively just copy both maps to TreeMap<BigDecimal, V>
and call .equals()
, but that requires copying both maps, whereas numberMapEquality()
only needs to copy one, avoids any copying if they're not the same size, and detects key collisions.