比较两个通用数字的值

b_e*_*erb 55 java generics numbers comparable

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

编辑:我试图用另一种解决方法TreeSets,这实际上自然排序工作(当然它的工作原理,所有的子类Number实现Comparable除的AtomicInteger和AtomicLong的).因此,我将丢失重复值.使用Lists时,Collection.sort()由于绑定不匹配,不接受我的列表.非常不满意.

gus*_*afc 31

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

class NumberComparator implements Comparator<Number> {

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

}
Run Code Online (Sandbox Code Playgroud)

但它仍然不是很好,因为它依赖于toString返回一个可解析的值BigDecimal(标准Java Number类所做的,但Number合同不要求).

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

  • 它不太可能,但如果我创建一个Number的子类并且不以正确的方式覆盖toString,它可能会导致NumberFormatException. (4认同)
  • @ user949300就像我说的那样,这是一个_可行的(但很脆弱)的解决方案_虽然不是很好-所以,我同意这是一个糟糕的解决方案!但是整个前提是有缺陷的,通常不应该使用任意的`Number`。最好的办法可能是进一步限制类型参数,将T扩展为Number&Comparable &lt;T&gt;。 (3认同)
  • *"toString"返回一个可由`BigDecimal`解析的值(标准的Java`Number`类所做的"*这不完全正确,因为`Double`和`Float`可以返回3个特殊值(`NaN`, `Infinity`和`-Infinity`)'不受'BigDecimal`支持. (3认同)

小智 31

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

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

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 它要求源集合是特定类型,因此它不适用于抽象Number类. (6认同)
  • 仅当一个方法的compareTo方法实际接受另一个方法时,这才有效。如果使用 Long 和 Double 调用,它将抛出 ClassCastException。 (3认同)

Lii*_*Lii 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
}
Run Code Online (Sandbox Code Playgroud)


rol*_*lve 13

在问了一个类似的问题并在这里研究答案后,我想出了以下内容.我认为它比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);
    }
}
Run Code Online (Sandbox Code Playgroud)


kop*_*per 5

最"通用"的Java原始数字是双倍的,所以使用简单

a.doubleValue() > b.doubleValue()
Run Code Online (Sandbox Code Playgroud)

在大多数情况下应该足够了,但是......在将数字转换为double时,这里存在一些微妙的问题.例如,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());
Run Code Online (Sandbox Code Playgroud)

结果是:

false
true
Run Code Online (Sandbox Code Playgroud)

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

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

  • 请注意,这也不适用于Long,因为它们也无法使用Double完全表示. (2认同)