이 질문에는 이미 답변이 있습니다.
누구든지 이유를 알고 있습니까?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
처음에 구현되었습니다.
가변성 외에도 여기에는 다른 고려 사항이 있습니다. 에이compareTo
에서의 구현Number
모든 숫자 값을BigDecimal
왜냐하면 그것은 모든Number
하위 유형. 수학과 성과 측면에서 그 진흥이 의미하는 바는 다소 불분명하지만, 내 직관은 그 해결책을 찾는다.
다음 표현을 언급 할 가치가 있습니다.
new Long(10).equals(new Integer(10))
항상false
, 어떤 점 또는 다른 것에서 모두를 위로 가게하는 경향이있는. 그래서 당신은 임의적 인 것을 비교할 수 없을뿐만 아니라Number
그러나 평등한지 여부를 판단 할 수도 없습니다.
또한 실제 프리미티브 유형 (float
,double
) 두 값이 같은지 판단하는 것은 까다로 우며 허용되는 오차 범위 내에서 수행되어야합니다. 다음과 같은 코드를 사용해보십시오 :
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()
instanceof 문장의 일련으로 devolve하지 않는 방법?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의 서브 클래스 다른 경우와 비교할 만하다. 같은 학급의 다른 것이있다. 복잡한 숫자와 같은 사례, 아니요 표준 총 주문이 존재하며, 하나는 정의 될 수있다. 에서 short, 하위 클래스가 아닌지의 여부 숫자는 다음과 같이 남겨 두어야합니다. 그 하위 클래스에 대한 결정.
비교할 수있는 숫자를 구현하려면 모든 하위 클래스 쌍에 대한 코드를 작성해야합니다. 그 대신 하위 클래스가 비교 가능하게 구현할 수 있도록 허용하는 것이 더 쉽습니다.
아마도 숫자를 비교하는 것이 비효율적 일 것이기 때문입니다. 모든 숫자가 이러한 비교를 허용 할 수있는 유일한 표현은 BigDecimal입니다.
대신, Number의 비 원자 서브 클래스는 Comparable 자체를 구현합니다.
원자는 변경 가능하므로 원자 비교를 구현할 수 없습니다.
당신이 사용할 수있는변형 (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);
원래의 문제 (숫자 목록 정렬)를 해결하기 위해 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 < Number, Object>, TreeSet
나만의 Comparator 작성
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
}
또 다른 옵션은 클래스 Number를 Comparable로 선언하고, compareTo 구현을 생략하고, Integer와 같은 일부 클래스에서 구현하고, AtomicInteger와 같은 다른 클래스에서는 UnsupportedException을 던집니다.
내 생각 엔 Comparable을 구현하지 않으면 구현할 클래스를 구현하는 데 더 많은 유연성을 제공한다는 것입니다. 모든 일반적인 숫자 (Integer, Long, Double 등)는 Comparable을 구현합니다. 요소 자체가 Comparable을 구현하는 한 Collections.sort를 호출 할 수 있습니다.
클래스 계층 구조를 봅니다. Long, Integer 등과 같은 래퍼 클래스는 Comparable을 구현합니다. 즉 정수는 정수와 비슷하고 long은 long과 비슷하지만 혼합 할 수는 없습니다. 최소한이 제네릭 패러다임으로. 나는 당신의 질문에 '왜'대답하는지 추측합니다.
byte
(프리미티브)는int
(원선). 프리미티브는 한 번에 하나의 값만 갖습니다.
언어 디자인 규칙은 이것을 허용합니다.
int i = 255
// down cast primitive
(byte) i == -1
에이Byte
~이 아니다.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)
방법?