127

이 질문에는 이미 답변이 있습니다.

누구든지 이유를 알고 있습니까?java.lang.Number구현하지 않는다.Comparable? 즉, 정렬 할 수 없습니다.Number와 함께Collections.sort약간 이상하게 보입니다.

토론 업데이트 게시 :

모든 유용한 답변 주셔서 감사합니다. 나는 끝내었다.이 주제에 대한 좀 더 많은 연구.

java.lang.Number가 Comparable을 구현하지 않는 이유에 대한 가장 간단한 설명은 변경 가능성에 대한 근원입니다.

약간의 검토를 위해,java.lang.Number추상적 인 수퍼 유형AtomicInteger,AtomicLong,BigDecimal,BigInteger,Byte,Double,Float,Integer,LongShort. 그 목록에,AtomicIntegerAtomicLong구현하지 않기Comparable.

주위를 파고 들며, 나는 그것을 실행하는 것이 좋은 습관이 아니라는 것을 발견했다.Comparable객체가 비교 도중 또는 후에 변경 될 수 있기 때문에 변경 가능한 유형에 대해서는 비교 결과를 쓸모 없게 렌더링합니다. 양자 모두AtomicLongAtomicInteger변경할 수 있습니다. API 설계자는Number도구Comparable미래의 하위 유형의 구현이 제한적 이었기 때문입니다. 과연,AtomicLongAtomicInteger오래 전에 Java 1.5에서 추가되었습니다.java.lang.Number처음에 구현되었습니다.

가변성 외에도 여기에는 다른 고려 사항이 있습니다. 에이compareTo에서의 구현Number모든 숫자 값을BigDecimal왜냐하면 그것은 모든Number하위 유형. 수학과 성과 측면에서 그 진흥이 의미하는 바는 다소 불분명하지만, 내 직관은 그 해결책을 찾는다.


12 답변


63

다음 표현을 언급 할 가치가 있습니다.

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자신의 인스턴스에.


  • Thxs Cletus. IMHO는 RealNumber (위 참조)와 같은 중간 숫자 유형이어야합니다. Float, Double, Long, Integer, Short를 캡슐화해야합니다. 그렇지 않으면 다형성이 부족하여 결국 해당 유형의 코드화되지 않은 기본 유형과 함께 코드 중복이 발생합니다. - Julien Chastang
  • Sun은 Number abstract를 작성하고 Comparable을 인터페이스로 포함 할 수있었습니다. 그렇다면 모든 특정 하위 클래스는 인스턴스가 없어도 해당 유형에 대해 유사하게 구현할 수 있습니다. 이것은 Number 형에 Comparable을 가지지 않는 것이 단점이라고 생각합니다. - Jason
  • @ Jason 그러면 숫자를 비교할 수있을 것으로 기대하지만 항상 작동하지 않기 때문에 혼란 스러울 것입니다. - mjaggard
  • 좀 더 혼란 스러울지도 모르지만 시스템을 이해하는 전문가에게는 그렇지 않을 수도 있습니다. 또한 전문가에게는 더 간단합니다. Collection API에서도 이와 동일한 단점이 있습니다. 특정 기본 구현에 대해 이해할 수없는 여러 가지 추상 메소드가 존재합니다. API에서 메소드를 제외하고 프로그래머가 구현 유형을 확인하도록 요구하지 않고 Sun은 메소드의 추상적 인 버전을 선택하고 구현이 메소드를 지원하지 않으면 예외를 throw하도록 권장했습니다. 만나다List.set,Map.put,Set.remove더 많은 수십. - Jason

42

대답은 Java bugparade를 참조하십시오.버그 4414323. 토론을 찾을 수도 있습니다.comp.lang.java.programmer

2001 년의 버그 보고서에 대한 Sun의 응답을 인용하십시오.

모든 "숫자"는 비교할 수 없습니다.   비교할 때 총 주문량은   숫자가 가능합니다. 이것조차도 아니다.   부동 소수점 숫자의 참; NaN   (숫자가 아님)   어떤 것보다 크거나 같지 않음   부동 소수점 값, 심지어 자체.   {Float, Double} .compare 합계를 부과합니다.   주문과 다른 주문   부동 소수점 "<" 및 "="   연산자. 또한 현재   구현되고있는, Number의 서브 클래스   다른 경우와 비교할 만하다.   같은 학급의 다른 것이있다.   복잡한 숫자와 같은 사례, 아니요   표준 총 주문이 존재하며,   하나는 정의 될 수있다. 에서   short, 하위 클래스가 아닌지의 여부   숫자는 다음과 같이 남겨 두어야합니다.   그 하위 클래스에 대한 결정.


  • 에디 : 링크를 가져 주셔서 감사합니다. 자바가 RealNumber 타입을 필요로한다는 버그 보고서에 동의합니다.이 타입은 상응하는 원시 숫자 타입 (예 : Float 등)을 가진 모든 숫자 타입의 수퍼 클래스입니다. Java로 숫자로 작업하는 것은 어려운 일입니다. - 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). Comparable을 구현하는 Double 또는 Float에 대한 논쟁이 될 수도 있지만 그렇습니다. - Pete Kirkham
  • 네, NaN 덕분에 모든 숫자를 비교할 수 없게되는 것은 다소 미친 일입니다. 모든 번호를 비교할 수 있다면 얼마나 좋을까요? 다른 언어를 사용하는 사람들에게도 이것이 얼마나 문제가 되는가? :) - Robert Grant

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


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 < Number, Object>, TreeSet 또는 Collections.sort (List , Comparator) 또는 Arrays.sort (Number [], Comparator)를 생성 할 수 있습니다.


1

나만의 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() + ", ");
 }
    }
}


  • 일부 부동 소수점 숫자에서는 올바르게 작동하지 않습니다. float의 내부 표현은 두 개의 float 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
}

또 다른 옵션은 클래스 Number를 Comparable로 선언하고, compareTo 구현을 생략하고, Integer와 같은 일부 클래스에서 구현하고, AtomicInteger와 같은 다른 클래스에서는 UnsupportedException을 던집니다.


0

내 생각 엔 Comparable을 구현하지 않으면 구현할 클래스를 구현하는 데 더 많은 유연성을 제공한다는 것입니다. 모든 일반적인 숫자 (Integer, Long, Double 등)는 Comparable을 구현합니다. 요소 자체가 Comparable을 구현하는 한 Collections.sort를 호출 할 수 있습니다.


0

클래스 계층 구조를 봅니다. Long, Integer 등과 같은 래퍼 클래스는 Comparable을 구현합니다. 즉 정수는 정수와 비슷하고 long은 long과 비슷하지만 혼합 할 수는 없습니다. 최소한이 제네릭 패러다임으로. 나는 당신의 질문에 '왜'대답하는지 추측합니다.


0

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)방법?

연결된 질문


관련된 질문

최근 질문