Java 比较两个通用数字的值

Java 比较两个通用数字的值,java,generics,numbers,comparable,Java,Generics,Numbers,Comparable,我想与变量进行比较,类型T扩展了Number。现在我想知道这两个变量中哪一个大于另一个或相等。不幸的是,我还不知道确切的类型,我只知道它将是java.lang.Number的子类型。我该怎么做 EDIT:我使用TreeSets尝试了另一种解决方法,这种方法实际上可以自然排序(当然,除了AtomicInteger和AtomicLong之外,Number的所有子类都可以实现Comparable)。因此,我将丢失重复的值。使用Lists时,Collection.sort()将不接受我的列表,因为绑定不

我想与变量进行比较,类型
T扩展了Number
。现在我想知道这两个变量中哪一个大于另一个或相等。不幸的是,我还不知道确切的类型,我只知道它将是
java.lang.Number
的子类型。我该怎么做

EDIT:我使用
TreeSet
s尝试了另一种解决方法,这种方法实际上可以自然排序(当然,除了AtomicInteger和AtomicLong之外,
Number
的所有子类都可以实现
Comparable
)。因此,我将丢失重复的值。使用
List
s时,
Collection.sort()
将不接受我的列表,因为绑定不匹配。很不满意

if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}
注意:不一定需要检查
instanceof
,这取决于您想要比较它们的准确程度。当然,您可以始终使用
.doubleValue()
,因为每个数字都应该提供列出的方法

编辑:如评论中所述,您将(始终)必须检查BigDecimal和friends。但它们提供了一种
.compareTo()
方法:

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 

您可以简单地使用
Number的doubleValue()
方法来比较它们;但是,您可能会发现结果不够精确,无法满足您的需要。

最“通用”的Java原语数是双倍的,因此使用

a.doubleValue() > b.doubleValue()
在大多数情况下应该足够了,但是。。。在将数字转换为双精度时,这里存在一些微妙的问题。例如,BigInteger可以实现以下功能:

    BigInteger a = new BigInteger("9999999999999992");
    BigInteger b = new BigInteger("9999999999999991");
    System.out.println(a.doubleValue() > b.doubleValue());
    System.out.println(a.doubleValue() == b.doubleValue());
结果:

false
true
虽然我预计这将是一个非常极端的情况,但这是可能的。没有-没有通用的100%准确的方法。数字接口没有像exactValue()这样的方法可以转换为某种类型,能够以完美的方式表示数字而不丢失任何信息


实际上,一般来说,拥有这样的完美数字是不可能的——例如,使用任何使用有限空间的算法来表示数字Pi是不可能的

我们假设您有如下方法:

public <T extends Number> T max (T a, T b) {
   ...
   //return maximum of a and b
}
public T max(T a,T b){
...
//返回a和b的最大值
}
如果您知道只有整数、long和double可以作为参数传递,则可以将方法签名更改为:

public <T extends Number> T max(double a, double b) {
   return (T)Math.max (a, b);
}
public T max(双a双b){
返回(T)Math.max(a,b);
}
这将适用于byte、short、integer、long和double

如果假定可以传递BigInteger或BigDecimal或浮点和double的混合,则无法创建一个通用方法来比较所有这些类型的参数。

有效(但脆弱)解决方案如下所示:

class NumberComparator implements Comparator<Number> {

    public int compare(Number a, Number b){
        return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
    }

}
classnumbercomparator实现比较器{
公共整数比较(数字a、数字b){
返回新的BigDecimal(a.toString()).compareTo(新的BigDecimal(b.toString());
}
}
尽管如此,它仍然不太好,因为它依赖于
toString
返回一个可由
BigDecimal
解析的值(标准Java
Number
类会这样做,但
Number
契约并不要求这样做)

编辑,七年后:正如评论中指出的,您需要考虑(至少?)三种特殊情况
toString

  • 无穷大
    ,它比一切都大,除了它本身是相等的
  • -无穷大
    ,它比任何事物都小,除了它本身是相等的
  • NaN
    ,这是一个非常复杂/不可能比较的问题

如果您的数字实例是原子的(即原子整数),那么您可以执行以下操作:

private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {

 Class<? extends Number> n1Class = n1.getClass();
 if (n1Class.isInstance(n2)) {
  Method compareTo = n1Class.getMethod("compareTo", n1Class);
  return (Integer) compareTo.invoke(n1, n2);
 }

 return -23;
}
private Integer compare(数字n1,数字n2)抛出SecurityException、NoSuchMethodException、IllegalArgumentException、IllegalAccessException、InvocationTargetException{

类这一个呢?当然不好,但它处理所有提到的必要情况

public class SimpleNumberComparator implements Comparator<Number>
    {
        @Override
        public int compare(Number o1, Number o2)
        {
            if(o1 instanceof Short && o2 instanceof Short)
            {
                return ((Short) o1).compareTo((Short) o2);
            }
            else if(o1 instanceof Long && o2 instanceof Long)
            {
                return ((Long) o1).compareTo((Long) o2);
            }
            else if(o1 instanceof Integer && o2 instanceof Integer)
            {
                return ((Integer) o1).compareTo((Integer) o2);
            }
            else if(o1 instanceof Float && o2 instanceof Float)
            {
                return ((Float) o1).compareTo((Float) o2);
            }
            else if(o1 instanceof Double && o2 instanceof Double)
            {
                return ((Double) o1).compareTo((Double) o2);
            }
            else if(o1 instanceof Byte && o2 instanceof Byte)
            {
                return ((Byte) o1).compareTo((Byte) o2);
            }
            else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
            {
                return ((BigInteger) o1).compareTo((BigInteger) o2);
            }
            else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
            {
                return ((BigDecimal) o1).compareTo((BigDecimal) o2);
            }
            else
            {
                throw new RuntimeException("Ooopps!");
            }

        }

    }
公共类SimpleNumberComparator实现Comparator
{
@凌驾
公共整数比较(数字o1,数字o2)
{
如果(o1短实例和o2短实例)
{
返回((短)o1)。与((短)o2)相比;
}
else if(长的o1实例和长的o2实例)
{
返回((长)o1)。与((长)o2)相比;
}
else if(o1整数实例和o2整数实例)
{
返回((整数)o1),比较((整数)o2);
}
else if(浮点数的o1实例和浮点数的o2实例)
{
返回((浮动)o1)。与((浮动)o2)相比;
}
else if(o1双精度实例和o2双精度实例)
{
返回((双)o1),与((双)o2)相比;
}
else if(o1字节实例和o2字节实例)
{
返回((字节)o1),比较((字节)o2);
}
else if(o1 instanceof BigInteger&&o2 instanceof BigInteger)
{
返回((BigInteger)o1),与((BigInteger)o2)进行比较;
}
else if(o1实例为BigDecimal&&o2实例为BigDecimal)
{
返回((BigDecimal)o1),与((BigDecimal)o2)进行比较;
}
其他的
{
抛出新的运行时异常(“Ooopps!”);
}
}
}

这应该适用于扩展数字的所有类,并且与它们自己相当

class NumberComparator<T extends Number> implements Comparator<T> {

    public int compare(T a, T b){
        if (a instanceof Comparable) 
            if (a.getClass().equals(b.getClass()))
                return ((Comparable<T>)a).compareTo(b);        
        throw new UnsupportedOperationException();
    }
}
classnumbercomparator实现比较器{
公共整数比较(TA,TB){
if(可比较的实例)
如果(a.getClass().equals(b.getClass()))
回报率((可比)a)。与(b)相比;
抛出新的UnsupportedOperationException();
}
}

这应该适用于扩展数字的所有类,并且与
class NumberComparator<T extends Number & Comparable> implements Comparator<T> {

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}
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);
    }
}
public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
    if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}

public void test() {
    compfunc(2, 1); // Works with Integer.
    compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
    compfunc(2, 1.0); // Compilation error! Different types.
    compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}
System.out.println(new BigDecimal(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1f).toPlainString());
System.out.println(Float.valueOf(0.1f).toString());
System.out.println(Float.valueOf(0.1f).doubleValue());