149

なぜ java.lang.Number実装しないのか誰かが知っていますComparableか?これは、私には少し奇妙に思えるNumbersを並べ替えることができないことを意味します。Collections.sort

ディスカッションの更新後:

すべての有益な回答をありがとう。私はこのトピックについてさらに調査を行うことになりました。

java.lang.NumberがComparableを実装しない理由の最も簡単な説明は、可変性の懸念に根ざしています。

少しレビューすると、は、、、、、、、、、、、およびの抽象的なスーパーjava.lang.Numberタイプです。そのリストにあり、実装しないでください。AtomicIntegerAtomicLongBigDecimalBigIntegerByteDoubleFloatIntegerLongShortAtomicIntegerAtomicLongComparable

Comparable掘り下げてみると、比較中または比較後にオブジェクトが変更され、比較の結果が役に立たなくなる可能性があるため、可変型に実装するのは適切ではないことがわかりました。AtomicLongとは両方ともAtomicInteger変更可能です。API設計者は、将来のサブタイプの実装を制約するため、Number実装しないことを事前に考えていました。Comparable確かに、AtomicLongそして最初に実装されAtomicIntegerてからずっと後にJava1.5で追加されました。java.lang.Number

可変性とは別に、ここでもおそらく他の考慮事項があります。のcompareTo実装では、すべてのサブタイプに対応できるため、Numberすべての数値をにプロモートする必要があります。数学とパフォーマンスの観点からのその昇進の意味は私には少し不明確ですが、私の直感はその解決策が厄介であると感じています。BigDecimalNumber

4

12 に答える 12

75

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

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

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

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

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().

Numberしたがって、Sun はs がComparable自分自身のインスタンスだけであるという (合理的な) 決定を下したと思います。

于 2009-01-26T21:20:59.177 に答える
46

答えについては、Javaバグパレードバグ4414323を参照してください。comp.lang.java.programmerからのディスカッションも見つけることができます

2001年のバグレポートに対するSunの回答から引用するには:

すべての「数値」は比較できません。比較対象は、番号の全順序付けが可能であることを前提としています。これは浮動小数点数にも当てはまりません。NaN(数値ではない)は、それ自体でさえ、浮動小数点値よりも小さくも大きくも等しくもありません。{Float、Double} .compareは、浮動小数点の「<」および「=」演算子の順序とは異なる全順序を課します。さらに、現在実装されているように、Numberのサブクラスは、同じクラスの他のインスタンスとのみ比較できます。複素数のように、標準の全順序付けが存在しない場合もありますが、定義することはできます。つまり、Numberのサブクラスが比較可能かどうかは、そのサブクラスの決定として残す必要があります。

于 2009-01-26T17:40:37.657 に答える
5

同等の数を実装するには、すべてのサブクラスペアのコードを作成する必要があります。代わりに、サブクラスに同等の実装を許可する方が簡単です。

于 2009-01-26T17:39:25.693 に答える
5

元の問題を解決する (数値のリストを並べ替える) ためのオプションは、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));
}
于 2012-10-18T12:36:38.917 に答える
3

おそらく、数値を比較するのはかなり非効率的であるためです。このような比較を可能にするためにすべての数値を適合させることができる唯一の表現は、BigDecimalです。

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

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

于 2009-01-26T17:38:05.023 に答える
3

Transmorphを使用すると、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);
于 2010-04-13T12:14:14.943 に答える
2

独自のコンパレータを作成する

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() + ", ");
 }
    }
}
于 2010-04-27T12:37:13.087 に答える
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
}

別のオプションは、クラス Number が Comparable を実装することを宣言し、compareTo 実装を省略し、Integer などの一部のクラスでそれを実装し、AtomicInteger などのクラスでは UnsupportedException をスローすることでした。

于 2011-12-08T16:07:09.440 に答える
1

異なるタイプの数値の標準的な比較はありません。ただし、独自の Comparator を作成し、それを使用して TreeMap<Number, Object>、TreeSet<Number> または Collections.sort(List<Number>, Comparator) または Arrays.sort(Number[], Comparator); を作成できます。

于 2009-01-26T22:04:22.070 に答える
0

私の推測では、Comparableを実装しないことで、クラスを実装して実装するかどうかをより柔軟に行うことができます。すべての一般的な数値(整数、長整数、倍精度など)はComparableを実装します。要素自体がComparableを実装している限り、Collections.sortを呼び出すことができます。

于 2009-01-26T17:38:45.350 に答える
0

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

于 2009-01-26T17:40:16.113 に答える
0

byte(プリミティブ) はint(プリミティブ) です。プリミティブには一度に 1 つの値しかありません。
言語設計規則により、これが可能になります。

int i = 255

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

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

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

aByteが anIntegerで anIntegerが a の場合、メソッドNumberで使用する値はどれですか?compareTo(Number number1, Number number2)

于 2009-01-26T18:50:33.427 に答える