69

我想比较类型的变量T extends Number。现在我想知道这两个变量中的哪一个大于或等于另一个。不幸的是,我还不知道确切的类型,我只知道它将是java.lang.Number. 我怎样才能做到这一点?

编辑:我尝试了另一种使用TreeSets 的解决方法,它实际上适用于自然排序(当然它有效,除了 AtomicInteger 和 AtomicLong 之外的所有Number实现子类)。Comparable因此我会丢失重复的值。使用Lists时,Collection.sort()由于绑定不匹配,不会接受我的列表。很不满意。

4

11 回答 11

41

这应该适用于所有扩展 Number 的类,并且与它们自己是 Comparable 的。通过添加 & Comparable,与 Sarmun 答案相比,您可以删除所有类型检查并免费提供运行时类型检查和错误抛出。

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

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}
于 2012-03-20T13:50:09.320 回答
36

一个工作(但脆弱)的解决方案是这样的:

class NumberComparator implements Comparator<Number> {

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

}

不过,它仍然不是很好,因为它依赖于toString返回一个可解析的值BigDecimal(标准 JavaNumber类会这样做,但Number合同并不要求这样做)。

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

于 2010-04-21T13:40:30.487 回答
16

在问了一个类似的问题并在这里研究了答案之后,我想出了以下内容。我认为它比 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);
    }
}
于 2012-10-14T16:15:43.707 回答
15

一种可能对您有用的解决方案是不使用T extends Number但使用T extends Number & Comparable. 这种类型意味着:“T只能设置为实现这两个接口的类型。”

这使您可以编写适用于所有可比数字的代码。静态类型和优雅。

这与 BennyBoy 提出的解决方案相同,但它适用于各种方法,不仅适用于比较器类。

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
}
于 2013-11-07T15:09:37.757 回答
6

最“通用”的 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% 准确的方法。Number 接口没有像exactValue() 这样的方法转换为能够以完美方式表示数字而不会丢失任何信息的某种类型。

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

于 2010-04-21T13:34:25.953 回答
2
if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}

注意:instanceof不一定需要检查 - 取决于您想要比较它们的确切程度。您当然可以简单地始终使用,因为每个 Number 都应提供此处.doubleValue()列出的方法。

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

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 
于 2010-04-21T13:23:20.997 回答
2

这个如何?绝对不是很好,但它处理了所有提到的必要情况。

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!");
            }

        }

    }
于 2010-04-21T22:19:50.170 回答
2

这应该适用于所有扩展 Number 的类,并且与它们自己是 Comparable 的。

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();
    }
}
于 2011-01-15T16:29:32.917 回答
1

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

于 2010-04-21T13:25:28.613 回答
0

假设您有一些方法,例如:

public <T extends Number> T max (T a, T b) {
   ...
   //return maximum of a and b
}

如果您知道只有整数、long 和 double 可以作为参数传递,那么您可以将方法签名更改为:

public <T extends Number> T max(double a, double b) {
   return (T)Math.max (a, b);
}

这适用于字节、短、整数、长和双精度。

如果您假定可以传递 BigInteger 或 BigDecimal 或浮点数和双精度数的混合,那么您无法创建一种通用方法来比较所有这些类型的参数。

于 2010-04-21T13:36:49.130 回答
0

如果您的 Number 实例从不是Atomic(即 AtomicInteger),那么您可以执行以下操作:

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;
}

这是因为所有非 Atomic 都Number实现 Comparable

编辑

由于反思,这是昂贵的:我知道

编辑 2

这当然不考虑您想要将小数与整数或其他类似的情况进行比较的情况......

编辑 3

这假设没有未实现 Comparable 的自定义 Number 后代(感谢@DJClayworth)

于 2010-04-21T13:47:05.200 回答