69

両方のタイプの変数と比較したいT extends Number。ここで、2 つの変数のどちらが他方よりも大きいか等しいかを知りたいと思います。残念ながら、正確な型はまだわかりません。 のサブタイプになることだけはわかっていますjava.lang.Number。どうやってやるの?

編集: TreeSets を使用して別の回避策を試しましたが、実際には自然順序付けで機能しました (もちろん、AtomicInteger と AtomicLong を除くNumber実装のすべてのサブクラスで機能します)。Comparableしたがって、重複する値が失われます。Listsを使用する場合Collection.sort()、バインドされた不一致によりリストが受け入れられません。非常に不満です。

4

11 に答える 11

41

これは、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 );
    }
}
于 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コントラクトでは要求されません) 、まだ優れているとは言えません。

編集、7 年後:コメントで指摘されているように、toString考慮する必要がある (少なくとも?) 3 つの特別なケースがあります。

于 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 に答える
6

最も「一般的な」Java プリミティブ数は double であるため、単純に使用する

a.doubleValue() > b.doubleValue()

ほとんどの場合はこれで十分ですが、数値を 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());

結果:

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 を拡張し、それ自体と比較可能であるすべてのクラスで機能するはずです。

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

これは、byte、short、integer、long、および double で機能します。

BigInteger または BigDecimal または float と double の組み合わせを渡すことができると仮定すると、これらすべてのタイプのパラメーターを比較する共通のメソッドを 1 つ作成することはできません。

于 2010-04-21T13:36:49.130 に答える
0

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

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

EDIT:

This is costly due to reflection: I know

EDIT 2:

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

EDIT 3:

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

于 2010-04-21T13:47:05.200 に答える