48

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

編集:私は別の回避策を試してみましたTreeSetsは、実際には自然な順序で動作しました(もちろんそれは動作します。Number実装するComparableAtomicIntegerとAtomicLongを除く)。したがって、重複した値を失うことになります。使用するときLists、Collection.sort()バウンドミスマッチのため私のリストを受け入れないでしょう。非常に満足できません。


  • うーん - これは関係している:stackoverflow.com/questions/480632/… - Jonik
  • これに"ジェネリック"のタグを付けないでください。 "一般番号"この場合、Java総称を参照しません。 - DJClayworth
  • Google CollectionsにはTreeMultiSetがあり、これを使用すると重複をなくすことなくソートできます。 - gustafc

11 답변


30

実用的な(しかし脆弱な)解決策は次のようなものです。

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年後:コメントで指摘されているように、(少なくとも)3つの特別な場合があります。toStringあなたが考慮する必要があることを生み出すことができます:


  • 可能性は低いですが、Numberのサブクラスを作成し、toStringを適切な方法でオーバーライドしないと、NumberFormatExceptionが発生する可能性があります。 - Roman
  • うまくいけば、プログラマーは、標準の子孫以外の数の子孫に遭遇するかどうかを知るでしょう。 - DJClayworth
  • ジーク!これは本当に悪い解決策です。私は手の届かないところでより良いものを考えることはできませんが、私はまだ下落しませんでした。 :-( - user949300
  • @ user949300私が言ったように、それは実用的な(しかし脆い)解決策どちらですか素晴らしいじゃないけど - それで、私はそれが悪い解決策であることに同意します!しかし、前提全体には欠陥があり、一般的には任意のものでは動作しないはずです。Numbers。ここで最も良いことは、おそらくtypeパラメータをさらに制約していることでしょう。T extends Number & Comparable<T>。 - gustafc
  • "toString解析可能な値を返すBigDecimal(これは標準のJavaNumberクラスは"を行いますそれを考えると、それは完全に真実ではありませんDoubleそしてFloat3つの特別な値を返すことができます(NaNInfinity、そして-Infinityそれはではないによる支援BigDecimal。 - Andreas

28

これは、Numberを拡張するすべてのクラスに対して機能し、それ自体と比較可能です。 &を追加してSarmunの回答と比較すると、すべての型チェックを削除でき、実行時の型チェックとエラースローを無料で提供できます。

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

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}


  • この答えに何か欠点はありますか?そうでなければ、おそらく最良の回答として選ばれるべきです。 - Campa
  • ソースコレクションは特定の型である必要があるため、抽象Numberクラスでは機能しません。 - RoninCoder

11

お願いした後似たような質問そしてここで答えを勉強して、私は以下を思いついた。私はそれが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);
    }
}


  • Float / Doubleを作成するには、コンストラクタESPECIALLYの代わりにBigDecimal.valueOfを使用する必要があります。そうしないと、変換時にbase2浮動小数点エラーが発生する可能性があります。 - Eric
  • ご意見ありがとうございます!あなたの意見が有効であるかどうかわかりません。 doubleコンストラクタのJavadocは次のように述べています。" [...]、このコンストラクタは正確な変換を提供することに注意してください。これを使用してdoubleをStringに変換するのと同じ結果は得られません。Double.toString(double)メソッドを使用してからBigDecimal(String)コンストラクタ。正確な変換は予想外の結果をもたらすことが多いのは事実です。0.1リテラル)。ただし、数字については何も知りませんので。起源、私はそれらを処理する唯一の賢明な方法はそれについて何も仮定しないことであると思います。 - rolve

11

あなたのために働くかもしれない一つの解決策は一緒に働かないことです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
}


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


5

最も「一般的な」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%正確な方法はありません。数値インタフェースには、exactValue()のように、情報を失うことなく完全な方法で数値を表すことができる何らかの型に変換するメソッドがありません。

実際にそのような完全な数を持つことは一般的に不可能です - 例えば、数Piを表すことは有限空間を使ったどんな算術を使っても不可能です。


  • Doubleでも完全に表現できないため、これもLongでは機能しません。 - Christopher Barber

2

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

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

編集する:コメントに述べられているように、あなたは(常に)BigDecimalとその友達をチェックしなければならないでしょう。しかしそれらは提供します.compareTo()方法:

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


  • どうですか?BigDecimalまたはダブルスの範囲外の他のタイプ? - b_erb
  • if(BigDecimalのyourNumberインスタンスとBigDecimalのotherNumberインスタンス){boolean greaterThanOtherNumber = yourNumber.compareTo(otherNumber)>を追加するだけです。 0; } - Tedil

1

あなたは単に使うことができますNumber's doubleValue()それらを比較する方法。しかし、結果があなたのニーズに対して十分に正確ではないことに気付くかもしれません。


  • これは危険すぎると思います - Yaneeve
  • BigIntegerとBigDecimalはNumberも実装しています。これではうまくいきません。 - Roman
  • 確かに、それは危険を伴う可能性があり、失敗する可能性があります。私が指摘した精度/精度の問題よりも多くの警告があります。ただし、大規模なBigDecimalsを処理する必要がない場合は、OPのニーズに合う可能性があります。 - Steven Mackenzie

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

        }

    }


  • これは、混在した番号タイプのリストでは機能しません - finnw
  • あなたが言ったように、いいじゃない、ここにいてはいけない:) - keni

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、あるいは浮動小数点数と倍精度数の組み合わせを渡すことができると仮定した場合、これらすべてのタイプのパラメーターを比較するための共通メソッドを1つ作成することはできません。


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

これはすべて非アトミックなのでNumbersを実装する

編集

これは反省のためにコストがかかります。

編集2

これはもちろん、あなたが小数点以下の桁数を整数またはいくつかのそのようなものと比較したい場合にはかかりません。

編集3

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


  • これは、Numberのカスタム定義の子孫がないことを前提としています。 - DJClayworth
  • @ DJクレイワース:その通り。そして、これらは私が上で述べたように唯一の制約ではありません。しかし、なぜNumberのカスタム定義の子孫が存在するのでしょうか。 - Yaneeve
  • のカスタム定義の子孫の例Numberですorg.apache.commons.math.fraction.BigFractionこれは私が自分のプログラムの多くで使用しています。 - finnw

リンクされた質問


関連する質問

最近の質問