127

この質問にはすでに答えがあります。

誰もがなぜ知っていますかjava.lang.Number実装していませんComparable?これはソートできないことを意味しますNumberCollections.sortこれは私には少し奇妙に思えます。

ディスカッションの投稿を投稿する:

すべての役に立つ回答をありがとう。私はやってしまったこのトピックに関するもう少しの研究

なぜjava.lang.NumberがComparableを実装しないのかについての最も簡単な説明は、可変性の問題に根ざしています。

ちょっとしたレビューのために、java.lang.Numberの抽象スーパータイプです。AtomicIntegerAtomicLongBigDecimalBigIntegerByteDoubleFloatIntegerLongそしてShort。そのリストでは、AtomicIntegerそしてAtomicLong実装しないComparable

調べてみると、実装することはお勧めできません。Comparable可変型の場合は、比較中または比較後にオブジェクトが変更される可能性があるため、比較の結果は役に立たなくなります。両方AtomicLongそしてAtomicInteger可変です。 APIデザイナーは、持っていないことを考えていましたNumber実装するComparable将来のサブタイプの実装が制限されていたためです。確かに、AtomicLongそしてAtomicIntegerずっと後にJava 1.5で追加されましたjava.lang.Number最初に実装されました。

可変性とは別に、ここでも他の考慮事項があります。 AcompareToの実装Numberすべての数値をに昇格する必要がありますBigDecimalそれはすべてを収容することができるのでNumberサブタイプ数学と業績の観点からその推進の意味は私には少し不明瞭ですが、私の直感ではその解決策がわかりにくいことがわかります。


12 답변


63

次の表現を言及する価値があります。

new Long(10).equals(new Integer(10))

常にfalse誰かがどこかの地点でつまずく傾向があります。だからあなたは任意を比較できないだけではないNumberしかし、それらが等しいかどうかを判断することすらできません。

また、実際のプリミティブ型(floatdouble2つの値が等しいかどうかを判断するのは難しいので、許容誤差範囲内で行う必要があります。次のようなコードを試してください。

double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
  d2 += 0.1d;
}
System.out.println(d2 - d1);

ちょっとした違いがあります。

それでは作りの問題に戻ってNumber Comparable。どのように実装しますか?のようなものを使うdoubleValue()確実にそれをしないでしょう。覚えているNumberサブタイプは次のとおりです。

  • Byte;
  • Short;
  • Integer;
  • Long;
  • AtomicInteger;
  • AtomicLong;
  • Float;
  • Double;
  • BigInteger;そして
  • BigDecimal

信頼できるコードを書いてもらえますかcompareTo()メソッドは一連のif instanceofステートメントにはしないのですか?Numberインスタンスには6つのメソッドしかありません。

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue();そして
  • doubleValue()

だから私は、Sunが(合理的な)決定を下したと思います。NumberだけだったComparable自分自身のインスタンスに。


  • Thxs Cletus私見はRealNumber(上記参照)のようなFloat、Double、Long、Integer、Shortをカプセル化した中間の数値型であるべきです。さもなければ、あなたは最終的にそれらの対応するボックス化されていないプリミティブ型と一緒にそれらの型のためのコード重複をもたらす多形性の欠如を持っています - Julien Chastang
  • SunはNumberを抽象化し、インターフェイスとしてComparableを含めることができました。その場合、すべての特定のサブクラスは、instanceofなしで、その型に匹敵するものを適切に実装できます。これは数値型で比較可能を持たないことの欠点であると思います。 - Jason
  • @Jasonそれから、あなたは数字を比較することができると期待するけれどもそれがいつもうまくいくというわけではないのでもっと混乱していたでしょう。 - mjaggard
  • それはもっと混乱するかもしれません…あるいはシステムを理解しているエキスパートユーザにとってはそうではないかもしれません。それはエキスパートユーザーにとってもより簡単でしょう。これと同じトレードオフがCollections APIでも行われています。基礎となる特定の実装には意味をなさない抽象メソッドがいくつかあります。 APIからメソッドを除外してプログラマに実装の種類をチェックさせる代わりに、Sunはメソッドの抽象バージョンを持つことを選択し、実装がそのメソッドをサポートしていない場合は例外をスローすることを推奨します。見るList.setMap.putSet.removeそして何十ももっと。 - Jason

42

答えは、Java bugparadeを参照してください。バグ4414323。また、からの議論を見つけることができますcomp.lang.java.programmer

2001年のバグ報告に対するSunの回答から引用するには:

すべての「数字」は比較可能ではありません。   比較可能な順序は、   数字は可能です。これは偶数ではありません   浮動小数点数に当てはまります。 NaN   (数字ではない)は、より小さくもない、   より大きい、または等しい   浮動小数点値、それ自体でも。   {Float、Double} .compareは合計を課します   注文とは異なる注文   浮動小数点数 "<"のと "="   演算子。また、現在のように   実装、Numberのサブクラス   他のインスタンスにしか匹敵しない   同じクラスの他にもあります   複素数のような場合   標準の合計順序が存在します。   1つ定義することもできますがに   short、のサブクラスかどうか   匹敵する数はとして残されるべきである   そのサブクラスに対する決定。


  • エディ:リンクをありがとう。私は、JavaがRealNumber型を必要とするというバグレポートに同意します。これは、対応する原始数値型(例えば、Floatなど)を持つすべての数値型のスーパークラスになります。 Javaで数字を操作することは、1がないと難しいままです。 - Julien Chastang
  • これは別の(やや不満足な)説明です。forums.sun.com/thread.jspa?threadID=5306968 - Julien Chastang
  • この回答を記事から直接引用して編集できる可能性はありますか。問題なくここに収まるように十分に短いです。 - Outlaw Programmer
  • 良いアイデア。完了しました。 - Eddie
  • Sunは、プログラマが上書きできる妥当なデフォルトを選択したかもしれません。これは申し訳ありません。空の文字列またはnull参照に関しては、同じ問題が文字列にも存在します。 NULL参照が文字列< foo"より大きいか小さいかSunは意味のある順序を選びました。 - Jason

4

同等の数を実装するためには、すべてのサブクラスのペアに対してコードを記述する必要があります。代わりに、サブクラスが同等のものを実装できるようにするほうが簡単です。


3

非常におそらくそれは数を比較することがかなり非効率的であろうから - そのような比較を可能にするためにすべての数が収まる唯一の表現はBigDecimalであろう。

代わりに、Numberの非アトミックサブクラスはComparable自体を実装しています。

アトミックなものは変更可能なので、アトミックな比較を実装することはできません。


  • そうではありません。すべての番号が全体の順序で互いに比較できるわけではありません。 - Eddie
  • 次に進みます - java.langのNumberのすべてのサブクラスは有理数です。実数の順序は全部あります。問題は何ですか? - Pete Kirkham
  • NaNは他の値と一緒に注文することはできません。それはそれ以上ではなく、それよりも大きくなく、そしてそれはそれ自体を含むいかなる値にも等しくありません。 - Eddie
  • 比較可能な既存の実装は、すでにそれを回避します(NaNを-Infinity以下にすることによるIIRC)。これは、DoubleまたはFloatのComparableの実装に対する反対意見にもなりますが、そうです。 - Pete Kirkham
  • ええ、NaNが原因ですべての数値を比較できないのはちょっと頭がおかしいです。すべての数値を比較できるとしたらどれほどクールでしょうか。また、これが問題であることを他の言語を使用する人々にとっては奇妙なことですか? :) - Robert Grant

3

あなたが使用することができますトランスモーフNumberComparatorクラスを使用して数値を比較します。

NumberComparator numberComparator = new NumberComparator();
assertTrue(numberComparator.compare(12, 24) < 0);
assertTrue(numberComparator.compare((byte) 12, (long) 24) < 0);
assertTrue(numberComparator.compare((byte) 12, 24.0) < 0);
assertTrue(numberComparator.compare(25.0, 24.0) > 0);
assertTrue(numberComparator.compare((double) 25.0, (float) 24.0) > 0);
assertTrue(numberComparator.compare(new BigDecimal(25.0), (float) 24.0) > 0);


3

元の問題を解決する(数字のリストをソートする)ための試みは、Numberを拡張し、Comparableを実装する一般的な型のリストを宣言することです。

何かのようなもの:

<N extends Number & Comparable<N>> void processNumbers(List<N> numbers) {
    System.out.println("Unsorted: " + numbers);
    Collections.sort(numbers);
    System.out.println("  Sorted: " + numbers);
    // ...
}

void processIntegers() {
    processNumbers(Arrays.asList(7, 2, 5));
}

void processDoubles() {
    processNumbers(Arrays.asList(7.1, 2.4, 5.2));
}


1

さまざまな種類の数値について標準的な比較はありません。 ただし、独自のComparatorを作成してそれを使用してTreeMap 、TreeSet またはCollections.sort(List 、Comparator)またはArrays.sort(Number []、Comparator)を作成することはできます。


1

あなた自身のコンパレータを書く

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class NumberComparator implements Comparator {
    @SuppressWarnings("unchecked")
    @Override
    public int compare(Number number1, Number number2) {
 if (((Object) number2).getClass().equals(((Object) number1).getClass())) {
     // both numbers are instances of the same type!
     if (number1 instanceof Comparable) {
  // and they implement the Comparable interface
  return ((Comparable) number1).compareTo(number2);
     }
 }
 // for all different Number types, let's check there double values
 if (number1.doubleValue() < number2.doubleValue())
     return -1;
 if (number1.doubleValue() > number2.doubleValue())
     return 1;
 return 0;
    }

    /**
     * DEMO: How to compare apples and oranges.
     */
    public static void main(String[] args) {
 ArrayList listToSort = new ArrayList();
 listToSort.add(new Long(10));
 listToSort.add(new Integer(1));
 listToSort.add(new Short((short) 14));
 listToSort.add(new Byte((byte) 10));
 listToSort.add(new Long(9));
 listToSort.add(new AtomicLong(2));
 listToSort.add(new Double(9.5));
 listToSort.add(new Double(9.0));
 listToSort.add(new Double(8.5));
 listToSort.add(new AtomicInteger(2));
 listToSort.add(new Long(11));
 listToSort.add(new Float(9));
 listToSort.add(new BigDecimal(3));
 listToSort.add(new BigInteger("12"));
 listToSort.add(new Long(8));
 System.out.println("unsorted: " + listToSort);
 Collections.sort(listToSort, new NumberComparator());
 System.out.println("sorted:   " + listToSort);
 System.out.print("Classes:  ");
 for (Number number : listToSort) {
     System.out.print(number.getClass().getSimpleName() + ", ");
 }
    }
}


  • これは一部の浮動小数点数では正しく機能しません。フロートを内部表現すると、2つのフロートAとBが等しくなるはずのときに、ごくわずかなマージンで等しくないことがあります。 - christopheml

1

なぜこれは悪い考えだったでしょうか。 :

abstract class ImmutableNumber extends Number implements Comparable {
    // do NOT implement compareTo method; allowed because class is abstract
}
class Integer extends ImmutableNumber {
    // implement compareTo here
}
class Long extends ImmutableNumber {
    // implement compareTo here
}

もう1つのオプションは、Number implements Comparableクラスを宣言し、compareTo実装を省略し、Integerなどの一部のクラスに実装し、AtomicIntegerなどのUnsupportedExceptionをスローすることです。


0

私は、Comparableを実装しないことで、それを実装するかどうかにかかわらず、クラスを実装する柔軟性が高まると思います。すべての一般的な数値(整数、長整数、倍精度など)は、比較可能を実装しています。要素自体がComparableを実装している限り、Collections.sortを呼び出すことができます。


0

クラス階層を見てください。 Long、IntegerなどのラッパークラスはComparableを実装しています。つまり、Integerは整数に匹敵し、longはlongに匹敵しますが、混在させることはできません。少なくともこのジェネリックスパラダイムでは。これがあなたの質問「なぜ」に答えると思います。


0

byte(原始)はint(原始)。プリミティブには一度に1つの値しかありません。

言語設計規則はこれを可能にします。

int i = 255

// down cast primitive
(byte) i == -1

AByteではありませんIntegerByteですNumberIntegerですNumberNumberオブジェクトは同時に複数の値を持つことができます。

Integer iObject = new Integer(255);
System.out.println(iObject.intValue());   // 255
System.out.println(iObject.byteValue());  // -1

もしByteですIntegerIntegerですNumber、あなたはどちらの値を使用しますかcompareTo(Number number1, Number number2)方法?

リンクされた質問


関連する質問

最近の質問