Comparing the values of two generic Numbers
I want to compare to variables, both of type T extends Number
. Now I want to know which of the two variables is greater than the other or equal. Unfortunately I don't know the exact type yet, I only know that it will be a subtype of java.lang.Number
. How can I do that?
EDIT: I tried another workaround using TreeSet
s, which actually worked with natural ordering (of course it works, all subclasses of Number
implement Comparable
except for AtomicInteger and AtomicLong). Thus I'll lose duplicate values. When using List
s, Collection.sort()
will not accept my list due to bound mismatchs. Very unsatisfactory.
This should work for all classes that extend Number, and are Comparable to themselves. By adding the & Comparable you allow to remove all the type checks and provides runtime type checks and error throwing for free when compared to Sarmun answer.
class NumberComparator<T extends Number & Comparable> implements Comparator<T> {
public int compare( T a, T b ) throws ClassCastException {
return a.compareTo( b );
}
}
A working (but brittle) solution is something like this:
class NumberComparator implements Comparator<Number> {
public int compare(Number a, Number b){
return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
}
}
It's still not great, though, since it counts on toString
returning a value parsable by BigDecimal
(which the standard Java Number
classes do, but which the Number
contract doesn't demand).
Edit, seven years later: As pointed out in the comments, there are (at least?) three special cases toString
can produce that you need to take into regard:
-
Infinity
, which is greater than everything, except itself to which it is equal -
-Infinity
, which is less than everything, except itself to which it is equal -
NaN
, which is extremely hairy/impossible to compare since all comparisons withNaN
result infalse
, including checking equality with itself.
After having asked a similar question and studying the answers here, I came up with the following. I think it is more efficient and more robust than the solution given by gustafc:
public int compare(Number x, Number y) {
if(isSpecial(x) || isSpecial(y))
return Double.compare(x.doubleValue(), y.doubleValue());
else
return toBigDecimal(x).compareTo(toBigDecimal(y));
}
private static boolean isSpecial(Number x) {
boolean specialDouble = x instanceof Double
&& (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
boolean specialFloat = x instanceof Float
&& (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
return specialDouble || specialFloat;
}
private static BigDecimal toBigDecimal(Number number) {
if(number instanceof BigDecimal)
return (BigDecimal) number;
if(number instanceof BigInteger)
return new BigDecimal((BigInteger) number);
if(number instanceof Byte || number instanceof Short
|| number instanceof Integer || number instanceof Long)
return new BigDecimal(number.longValue());
if(number instanceof Float || number instanceof Double)
return new BigDecimal(number.doubleValue());
try {
return new BigDecimal(number.toString());
} catch(final NumberFormatException e) {
throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
}
}