Java.lang.number가 비교할 수없는 이유는 무엇입니까? [복제하다
-
20-08-2019 - |
문제
이 질문은 이미 여기에 답이 있습니다.
- 두 가지 일반 숫자의 값 비교 12 답변
그 이유를 아는 사람이 있습니까? java.lang.Number
구현되지 않습니다 Comparable
? 이것은 당신이 정렬 할 수 없다는 것을 의미합니다 Number
S와 함께 Collections.sort
나에게 조금 이상해 보인다.
토론 후 업데이트 :
모든 유용한 응답에 감사드립니다. 나는 끝났다 이 주제에 대한 더 많은 연구.
Java.lang.number가 비교할 수없는 이유에 대한 가장 간단한 설명은 돌연변이 문제에 근거가 있습니다.
약간의 검토를 위해 java.lang.Number
추상 슈퍼 타입입니다 AtomicInteger
, AtomicLong
, BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
그리고 Short
. 그 목록에서 AtomicInteger
그리고 AtomicLong
구현하지 않기 위해 Comparable
.
파고 들면서, 나는 구현하는 것이 좋은 관행이 아니라는 것을 알았습니다. Comparable
비교 결과가 쓸모없는 결과를 렌더링하는 비교 중 또는 비교 후에 객체가 변경 될 수 있기 때문에 변한 유형에서. 둘 다 AtomicLong
그리고 AtomicInteger
변이 가능합니다. API 디자이너는 예측하지 못했습니다 Number
구현하다 Comparable
미래 하위 유형의 구현을 제한했을 것입니다. 물론, AtomicLong
그리고 AtomicInteger
1.5 년 후에 Java에 추가되었습니다 java.lang.Number
처음에 구현되었습니다.
돌연변이와는 별도로 여기에는 다른 고려 사항도있을 수 있습니다. ㅏ compareTo
구현 Number
모든 숫자 값을 홍보해야합니다 BigDecimal
모든 것을 수용 할 수 있기 때문입니다 Number
하위 유형. 수학과 공연 측면에서 그 홍보의 의미는 나에게는 불분명하지만 직관은 그 해결책을 kludgy로 발견합니다.
해결책
다음 표현을 언급 할 가치가 있습니다.
new Long(10).equals(new Integer(10))
항상 false
, 그것은 어느 시점에서 모든 사람을 끌어 올리는 경향이 있습니다. 따라서 임의적 인 비교할 수는 없습니다 Number
s 그러나 당신은 그들이 평등한지 아닌지를 판단 할 수 없습니다.
또한 실제 원시 유형 (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()
IF 인스턴스 명령문으로 일련의 진술에 빠지지 않는 메소드? Number
인스턴스에는 6 가지 방법 만 사용할 수 있습니다.
byteValue()
;shortValue()
;intValue()
;longValue()
;floatValue()
; 그리고doubleValue()
.
그래서 나는 Sun이 (합리적인) 결정을 내린 것 같습니다. Number
S는 단지 Comparable
자신의 경우에.
다른 팁
답은 Java Bugparade를 참조하십시오 버그 4414323. 당신은 또한 토론을 찾을 수 있습니다 comp.lang.java.programmer
2001 년의 버그 보고서에 대한 태양 반응에서 인용 :
모든 "숫자"는 비교할 수 없습니다. 비슷한 숫자 순서가 가능하다고 가정합니다. 이것은 부동 소수점 숫자에도 해당되지 않습니다. NAN (숫자가 아님)은 그 자체로도 떠 다니는 지점 값보다 크거나 크거나 동일하지 않습니다. {float, double}. compare는 플로팅 포인트 "<"및 "="연산자의 순서와 다른 총 순서를 부과합니다. 또한 현재 구현 된 바와 같이, 숫자의 서브 클래스는 동일한 클래스의 다른 인스턴스와 비슷합니다. 표준 총 순서가 존재하지 않는 복소수와 같은 다른 경우가 있습니다. 요컨대, 숫자의 서브 클래스가 비교할 수 있는지 여부는 해당 서브 클래스의 결정으로 남겨 져야합니다.
숫자에 비해 비슷한 구현하려면 모든 서브 클래스 쌍에 대한 코드를 작성해야합니다. 대신 서브 클래스가 비슷한 구현을 허용하는 것이 더 쉽습니다.
아마도 숫자를 비교하는 것이 다소 비효율적 일 것이기 때문에, 그러한 비교를 허용하기 위해 모든 숫자가 적합 할 수있는 유일한 표현은 큰 것이 될 것입니다.
대신, 숫자의 비 원자 서브 클래스는 그 자체로 비슷합니다.
원자는 변이 가능하므로 원자 비교를 구현할 수 없습니다.
당신이 사용할 수있는 트랜스 모프 숫자를 사용하여 숫자를 비교합니다.
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);
원래 문제를 해결하려면 (숫자 목록을 정렬) 옵션은 일반 유형 확장 숫자의 목록을 선언하고 비슷한 구현입니다.
같은 것 :
<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));
}
다른 유형의 수에 대한 스타드 비교는 없습니다. 그러나 자신의 비교기를 작성하여 Treemap을 만들 수 있습니다.u003CNumber, Object> , 트리 셋u003CNumber> 또는 collections.sort (목록u003CNumber> , 비교) 또는 배열 .SORT (number [], 비교기);
나만의 비교기를 작성하십시오
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
}
또 다른 옵션은 클래스 번호 구현을 비교할 수있는 선언하고, 비교 구현을 생략하고, Integer와 같은 일부 클래스에서 구현하는 동안 Atomicinteger와 같은 다른 사람들에게는 지원되지 않은 외환을 던지는 것입니다.
내 생각에 비슷한 구현을 구현하지 않으면 클래스를 구현하여 구현할 수있는 유연성을 더 많이 제공 할 것입니다. 모든 공통 숫자 (정수, 길고, 이중 등)는 비슷한 구현 가능합니다. 요소 자체가 비슷한 구현하는 한 여전히 Collections.sort를 호출 할 수 있습니다.
클래스 계층 구조를보고 있습니다. 긴, 정수 등과 같은 래퍼 클래스, 비슷한 구현, 즉 정수는 정수와 비교할 수 있으며 긴 길이와 비슷하지만 혼합 할 수는 없습니다. 적어도이 제네릭 패러다임으로. 나는 당신의 질문에 '왜' '라고 생각합니다.
byte
(원시)은 a int
(원어). 프리미티브는 한 번에 하나의 값 만 있습니다.
언어 설계 규칙이이를 허용합니다.
int i = 255
// down cast primitive
(byte) i == -1
ㅏ Byte
아닙니다 Integer
. Byte
a Number
그리고 Integer
a Number
. Number
객체는 동시에 하나 이상의 값을 가질 수 있습니다.
Integer iObject = new Integer(255);
System.out.println(iObject.intValue()); // 255
System.out.println(iObject.byteValue()); // -1
만약 Byte
이다 Integer
그리고 Integer
a Number
, 당신은 당신이 어떤 값을 사용할 것인지 compareTo(Number number1, Number number2)
방법?