web-dev-qa-db-ja.com

Java.lang.NumberがComparableを実装しないのはなぜですか?

Java.lang.NumberComparableを実装しない理由を誰もが知っていますか?これは、Collections.sortNumbersをソートできないことを意味します。

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

有益な回答をありがとう。最終的に このトピックに関するさらに詳しい調査 を行いました。

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

少しレビューすると、Java.lang.Numberは、AtomicIntegerAtomicLongBigDecimalBigIntegerByteDoubleFloatIntegerLong、およびShortの抽象スーパータイプです。そのリストで、AtomicIntegerAtomicLongComparableを実装しません。

掘り下げてみると、比較中または後にオブジェクトが変更されて比較結果が役に立たなくなる可能性があるため、可変型にComparableを実装することはお勧めできません。 AtomicLongAtomicIntegerは両方とも可変です。 API設計者は、将来のサブタイプの実装に制約があるため、NumberComparableを実装させないという先見の明を持っていました。確かに、Java.lang.Numberが最初に実装されてから長い間、Java 1.5にAtomicLongAtomicIntegerが追加されました。

可変性とは別に、ここでもおそらく他の考慮事項があります。 compareToNumber実装は、すべての数値をBigDecimalにプロモートする必要があります。これは、Numberサブタイプをすべて収容できるためです。数学とパフォーマンスの面でのこのプロモーションの意味は、私には少しわかりませんが、私の直感では、その解決策は気が狂っています。

131
Julien Chastang

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

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

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

また、実際のプリミティブ型(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);
_

わずかな違いが残ります。

NumberComparableを作成する問題に戻ります。どのように実装しますか? doubleValue()のようなものを使用すると、確実に実行されません。 Numberサブタイプは次のとおりです。

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

一連のif instanceofステートメントに委ねられない信頼できるcompareTo()メソッドをコーディングできますか? Numberインスタンスには、6つのメソッドしか使用できません。

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

ですから、SunはNumbersは自分自身のインスタンスに対してのみComparableであるという(合理的な)決定を下したと思います。

63
cletus

答えについては、Java bugparade bug 441432 を参照してください。 comp.lang.Java.programmer から議論を見つけることもできます。

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

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

42
Eddie

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

4
z -

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

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

アトミックなものは可変なので、アトミックな比較を実装できません。

3
Pete Kirkham

元の問題の解決(数値のリストのソート)を試みるためのオプションは、数値を拡張して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));
}
3
Jaime Saiz

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);
3
cchabanois

なぜこれが悪い考えだったのでしょうか? :

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実装を省略し、AtomicIntegerなどの他のクラスでUnsupportedExceptionをスローしながら、Integerなどの一部のクラスで実装することでした。

1
Raigedas

さまざまなタイプの数値について、標準的な比較はありません。ただし、独自のコンパレータを作成し、それを使用してTreeMap <Number、Object>、TreeSet <Number>またはCollections.sort(List <Number>、Comparator)またはArrays.sort(Number []、Comparator)を作成できます。

1
Peter Lawrey

独自のコンパレータを書く

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() + ", ");
 }
    }
}
1
Hilmar

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

_int i = 255

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

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

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

ByteIntegerであり、IntegerNumberである場合、compareTo(Number number1, Number number2)メソッドで使用する値はどれですか。

0
l_39217_l

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

0
Steve Kuo

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

0
airportyh