Java 比较两个通用数字的值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2683202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:46:00  来源:igfitidea点击:

Comparing the values of two generic Numbers

javagenericsnumberscomparable

提问by b_erb

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?

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

EDIT: I tried another workaround using TreeSets, which actually worked with natural ordering (of course it works, all subclasses of Numberimplement Comparableexcept for AtomicInteger and AtomicLong). Thus I'll lose duplicate values. When using Lists, Collection.sort()will not accept my list due to bound mismatchs. Very unsatisfactory.

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

采纳答案by gustafc

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 toStringreturning a value parsable by BigDecimal(which the standard Java Numberclasses do, but which the Numbercontract doesn't demand).

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

Edit, seven years later:As pointed out in the comments, there are (at least?) three special cases toStringcan produce that you need to take into regard:

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

回答by Tedil

if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}

Note:The instanceofcheck isn't necessarily needed - depends on how exactly you want to compare them. You could of course simply always use .doubleValue(), as every Number should provide the methods listed here.

注:instanceof不是必需的检查-取决于你想如何准确地对它们进行比较。您当然可以简单地始终使用.doubleValue(),因为每个 Number 都应提供此处列出的方法。

Edit: As stated in the comments, you will (always) have to check for BigDecimal and friends. But they provide a .compareTo()method:

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

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

回答by Steven Mackenzie

You can simply use Number's doubleValue()method to compare them; however you may find the results are not accurate enough for your needs.

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

回答by kopper

The most "generic" Java primitive number is double, so using simply

最“通用”的 Java 原始数是 double,所以简单地使用

a.doubleValue() > b.doubleValue()

should be enough in most cases, but... there are subtle issues here when converting numbers to double. For example the following is possible with BigInteger:

在大多数情况下应该足够了,但是……将数字转换为双精度数时存在一些微妙的问题。例如,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());

results in:

结果是:

false
true

Although I expect this to be very extreme case this is possible. And no - there is no generic 100% accurate way. Number interface have no method like exactValue() converting to some type able to represent number in perfect way without loosing any information.

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

Actually having such perfect numbers is impossible in general - for example representing number Pi is impossible using any arithmetic using finite space.

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

回答by Roman

Let's assume that you have some method like:

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

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

If you know that there are only integers, longs and doubles can be passed as parameters then you can change method signature to:

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

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

This will work for byte, short, integer, long and double.

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

If you presume that BigInteger's or BigDecimal's or mix of floats and doubles can be passed then you cannot create one common method to compare all these types of parameters.

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

回答by Yaneeve

If your Number instances are neverAtomic (ie AtomicInteger) then you can do something like:

如果您的 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;
}

This is since all non-Atomic Numbers implement Comparable

这是因为所有非原子Number的都实现了 Comparable

EDIT:

编辑

This is costly due to reflection: I know

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

EDIT 2:

编辑 2

This of course does not take of a case in which you want to compare decimals to ints or some such...

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

EDIT 3:

编辑 3

This assumes that there are no custom-defined descendants of Number that do not implement Comparable (thanks @DJClayworth)

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

回答by b_erb

What about this one? Definitely not nice, but it deals with all necessary cases mentioned.

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

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

        }

    }

回答by Sarmun

This should work for all classes that extend Number, and are Comparable to themselves.

这应该适用于所有扩展 Number 的类,并且可以与它们自己进行比较。

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

回答by BennyBoy

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.

这应该适用于所有扩展 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 );
    }
}

回答by rolve

After having asked a similar questionand 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:

在提出了类似的问题并在这里研究了答案之后,我想出了以下内容。我认为它比 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);
    }
}