この質問にはすでに答えがあります。
誰もがなぜ知っていますかjava.lang.Number
実装していませんComparable
?これはソートできないことを意味しますNumber
とCollections.sort
これは私には少し奇妙に思えます。
ディスカッションの投稿を投稿する:
すべての役に立つ回答をありがとう。私はやってしまったこのトピックに関するもう少しの研究。
なぜjava.lang.NumberがComparableを実装しないのかについての最も簡単な説明は、可変性の問題に根ざしています。
ちょっとしたレビューのために、java.lang.Number
の抽象スーパータイプです。AtomicInteger
、AtomicLong
、BigDecimal
、BigInteger
、Byte
、Double
、Float
、Integer
、Long
そしてShort
。そのリストでは、AtomicInteger
そしてAtomicLong
実装しないComparable
。
調べてみると、実装することはお勧めできません。Comparable
可変型の場合は、比較中または比較後にオブジェクトが変更される可能性があるため、比較の結果は役に立たなくなります。両方AtomicLong
そしてAtomicInteger
可変です。 APIデザイナーは、持っていないことを考えていましたNumber
実装するComparable
将来のサブタイプの実装が制限されていたためです。確かに、AtomicLong
そしてAtomicInteger
ずっと後にJava 1.5で追加されましたjava.lang.Number
最初に実装されました。
可変性とは別に、ここでも他の考慮事項があります。 AcompareTo
の実装Number
すべての数値をに昇格する必要がありますBigDecimal
それはすべてを収容することができるのでNumber
サブタイプ数学と業績の観点からその推進の意味は私には少し不明瞭ですが、私の直感ではその解決策がわかりにくいことがわかります。
次の表現を言及する価値があります。
new Long(10).equals(new Integer(10))
常にfalse
誰かがどこかの地点でつまずく傾向があります。だからあなたは任意を比較できないだけではないNumber
しかし、それらが等しいかどうかを判断することすらできません。
また、実際のプリミティブ型(float
、double
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()
。だから私は、Sunが(合理的な)決定を下したと思います。Number
だけだったComparable
自分自身のインスタンスに。
List.set
、Map.put
、Set.remove
そして何十ももっと。 - Jason
答えは、Java bugparadeを参照してください。バグ4414323。また、からの議論を見つけることができますcomp.lang.java.programmer
2001年のバグ報告に対するSunの回答から引用するには:
すべての「数字」は比較可能ではありません。 比較可能な順序は、 数字は可能です。これは偶数ではありません 浮動小数点数に当てはまります。 NaN (数字ではない)は、より小さくもない、 より大きい、または等しい 浮動小数点値、それ自体でも。 {Float、Double} .compareは合計を課します 注文とは異なる注文 浮動小数点数 "<"のと "=" 演算子。また、現在のように 実装、Numberのサブクラス 他のインスタンスにしか匹敵しない 同じクラスの他にもあります 複素数のような場合 標準の合計順序が存在します。 1つ定義することもできますがに short、のサブクラスかどうか 匹敵する数はとして残されるべきである そのサブクラスに対する決定。
同等の数を実装するためには、すべてのサブクラスのペアに対してコードを記述する必要があります。代わりに、サブクラスが同等のものを実装できるようにするほうが簡単です。
非常におそらくそれは数を比較することがかなり非効率的であろうから - そのような比較を可能にするためにすべての数が収まる唯一の表現はBigDecimalであろう。
代わりに、Numberの非アトミックサブクラスはComparable自体を実装しています。
アトミックなものは変更可能なので、アトミックな比較を実装することはできません。
あなたが使用することができますトランスモーフ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);
元の問題を解決する(数字のリストをソートする)ための試みは、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));
}
さまざまな種類の数値について標準的な比較はありません。
ただし、独自のComparatorを作成してそれを使用してTreeMap
あなた自身のコンパレータを書く
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() + ", ");
}
}
}
なぜこれは悪い考えだったでしょうか。 :
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をスローすることです。
私は、Comparableを実装しないことで、それを実装するかどうかにかかわらず、クラスを実装する柔軟性が高まると思います。すべての一般的な数値(整数、長整数、倍精度など)は、比較可能を実装しています。要素自体がComparableを実装している限り、Collections.sortを呼び出すことができます。
クラス階層を見てください。 Long、IntegerなどのラッパークラスはComparableを実装しています。つまり、Integerは整数に匹敵し、longはlongに匹敵しますが、混在させることはできません。少なくともこのジェネリックスパラダイムでは。これがあなたの質問「なぜ」に答えると思います。
byte
(原始)はint
(原始)。プリミティブには一度に1つの値しかありません。
言語設計規則はこれを可能にします。
int i = 255
// down cast primitive
(byte) i == -1
AByte
ではありませんInteger
。Byte
ですNumber
とInteger
ですNumber
。Number
オブジェクトは同時に複数の値を持つことができます。
Integer iObject = new Integer(255);
System.out.println(iObject.intValue()); // 255
System.out.println(iObject.byteValue()); // -1
もしByte
ですInteger
とInteger
ですNumber
、あなたはどちらの値を使用しますかcompareTo(Number number1, Number number2)
方法?