web-dev-qa-db-ja.com

2つの一般的な数値の値を比較する

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

[〜#〜] edit [〜#〜]TreeSetsを使用して別の回避策を試しましたが、実際には自然な順序で機能しました(もちろん、Number AtomicIntegerおよびAtomicLongを除くComparableを実装します。したがって、重複した値は失われます。 Listsを使用する場合、Collection.sort()はバインドされた不一致のためにリストを受け入れません。非常に不十分です。

54
b_erb

動作する(しかし脆弱な)ソリューションは次のようなものです。

class NumberComparator implements Comparator<Number> {

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

}

ただし、toStringで解析可能な値を返すBigDecimal(標準のJava Numberクラスが行うが、 Number契約は要求しません)。

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

30
gustafc

これは、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 );
    }
}
32
BennyBoy

あなたのために働くかもしれない1つの解決策は、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
}
13
Lii

類似した質問 を尋ねて、ここで答えを勉強した後、私は次のことを思いつきました。 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);
    }
}
12
rolve

最も「一般的な」Javaプリミティブ番号は2倍なので、単純に

a.doubleValue() > b.doubleValue()

ほとんどの場合はこれで十分ですが、...数値を2倍に変換するときに微妙な問題があります。たとえば、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を表すことは不可能です。

5
kopper

これは、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();
    }
}
2
Sarmun
_if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}
_

注:instanceofチェックは必ずしも必要ではありません-それらをどの程度正確に比較したいかによって異なります。もちろん、すべてのNumberが here にリストされているメソッドを提供する必要があるため、常に常に.doubleValue()を使用できます。

Edit:コメントで述べたように、BigDecimalと友人を(常に)チェックする必要があります。しかし、それらは.compareTo()メソッドを提供します:

_if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 
_
2
Tedil

単純にNumber's doubleValue()メソッドを使用してそれらを比較できます。ただし、結果がニーズに対して十分に正確でない場合があります。

1

これはどうですか?確かにニースではありませんが、言及されているすべての必要なケースを扱っています。

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

        }

    }
1
b_erb
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());
0
t _ liang

Numberインスタンスがnever 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;
}

これは、すべての非アトミックNumbersがComparableを実装しているためです

[〜#〜] edit [〜#〜]

これは反射のために費用がかかります:私は知っています

編集2

もちろん、これは小数をintなどと比較したい場合を取りません...

編集

これは、Comparableを実装しないNumberのカスタム定義の子孫がないことを前提としています(@DJClayworthに感謝)

0
Yaneeve

次のようなメソッドがあると仮定しましょう。

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

整数、longs、およびdoublesのみがパラメーターとして渡されることがわかっている場合は、メソッドシグネチャを次のように変更できます。

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つ作成することはできません。

0
Roman