48

나는 두 변수와 비교하기를 원한다.T extends Number. 이제 저는 두 변수 중 어느 것이 다른 변수보다 더 큰지 알고 싶습니다. 불행히도 정확한 유형을 아직 알지 못하지만 하위 유형이 될 것임을 알고 있습니다.java.lang.Number. 어떻게해야합니까?

편집하다: 다른 해결 방법을 사용해 보았습니다.TreeSet실제로는 자연 순서와 함께 작동합니다 (물론 작동합니다, 모든 하위 클래스의Number도구ComparableAtomicInteger 및 AtomicLong 제외). 따라서 나는 중복 된 가치를 잃을 것이다. 사용할 때List에스,Collection.sort()바인딩 된 불일치로 인해 내 목록을 허용하지 않습니다. 매우 만족스럽지 않습니다.


  • 흠 - 관련이 있습니다.stackoverflow.com/questions/480632/… - Jonik
  • 이 태그에는 '제네릭'이라는 태그가 지정되어서는 안됩니다. " 일반 번호 " 이 경우 Java generics를 참조하지 않습니다. - DJClayworth
  • Google Collections에는 중복을 잃지 않고 항목을 정렬 할 수있는 TreeMultiSet이 있습니다. - gustafc

11 답변


30

작동하는 (그러나 부서지기 쉬운) 솔루션은 다음과 같습니다.

class NumberComparator implements Comparator<Number> {

    public int compare(Number a, Number b){
        return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
    }

}

그래도별로 좋지는 않습니다.toString에 의해 파싱 가능한 값을 반환BigDecimal(표준 자바Number수업은하지만,Number계약은 요구하지 않는다).

편집, 7 년 후 :코멘트에서 지적한 바와 같이, (최소한?) 세 가지 특수한 경우가 있습니다toString다음을 고려해야합니다.


  • 그럴 가능성은 없지만 Number 서브 클래스를 만들고 적절한 방법으로 toString을 재정의하지 않으면 NumberFormatException이 발생할 수 있습니다. - Roman
  • 다행히 표준 프로그래머가 아닌 다른 Number의 자손이 있는지 알아볼 수 있기를 바랍니다. - DJClayworth
  • 지크! 이것은 정말 나쁜 해결책입니다. 손을 떼지 않아도 더 좋은 것을 생각할 수는 없지만 아직 다운 보턴을 얻지는 못했습니다. :-( - user949300
  • @ user949300 내가 말했듯이, 그것은작동 (그러나 부서지기 쉬운) 해결책그것은그래도 대단하지 않아.- 그래서 나는 나쁜 해결책이라는 데 동의한다! 그러나 전체 전제에는 결함이있어 일반적으로 임의적으로 작업해서는 안됩니다Number에스. 여기서 가장 좋은 것은 아마도 유형 매개 변수를T extends Number & Comparable<T>. - gustafc
  • "toString에 의해 파싱 가능한 값을 반환BigDecimal(표준 자바Number수업은 "그것은 전적으로 사실이 아닙니다.DoubleFloat3 개의 특수 값을 반환 할 수 있습니다 (NaN,Infinity, 및-Infinity) 그것은아니~에 의해 지원되는BigDecimal. - Andreas

28

이것은 Number를 확장하고 자신과 비교할 수있는 모든 클래스에서 작동합니다. & 비교하면 Sarmun 응답과 비교할 때 모든 유형 검사를 제거 할 수 있으며 런타임 유형 검사와 오류 던져를 무료로 제공합니다.

class NumberComparator<T extends Number & Comparable> implements Comparator<T> {

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}


  • 이 대답에 결점이 있습니까? 그렇지 않으면 아마도 최선의 답변으로 선택되어야합니다. - Campa
  • 소스 컬렉션을 특정 유형으로 지정해야하므로 추상 Number 클래스에서 작동하지 않습니다. - RoninCoder

11

요청한 후비슷한 질문여기에 대한 답을 공부하면서 다음과 같은 것을 생각해 냈습니다. 나는 gustafc가 제공 한 솔루션보다 더 효율적이고 강력하다고 생각합니다.

public int compare(Number x, Number y) {
    if(isSpecial(x) || isSpecial(y))
        return Double.compare(x.doubleValue(), y.doubleValue());
    else
        return toBigDecimal(x).compareTo(toBigDecimal(y));
}

private static boolean isSpecial(Number x) {
    boolean specialDouble = x instanceof Double
            && (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
    boolean specialFloat = x instanceof Float
            && (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
    return specialDouble || specialFloat;
}

private static BigDecimal toBigDecimal(Number number) {
    if(number instanceof BigDecimal)
        return (BigDecimal) number;
    if(number instanceof BigInteger)
        return new BigDecimal((BigInteger) number);
    if(number instanceof Byte || number instanceof Short
            || number instanceof Integer || number instanceof Long)
        return new BigDecimal(number.longValue());
    if(number instanceof Float || number instanceof Double)
        return new BigDecimal(number.doubleValue());

    try {
        return new BigDecimal(number.toString());
    } catch(final NumberFormatException e) {
        throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
    }
}


  • Float / Double 생성을 위해 ESPECIALLY 생성자 대신 BigDecimal.valueOf를 사용해야하거나 변환시 base2 부동 소수점 오류가 발생합니다. - Eric
  • 귀하의 의견을 보내 주셔서 감사합니다! 그래도 귀하의 요점은 유효하지 않습니다. 이중 생성자의 Javadoc은 다음과 같이 말합니다. "[...],이 생성자는 정확한 변환을 제공합니다. 그것은 double을 String으로 변환하는 것과 같은 결과를주지 않는다.Double.toString(double)방법을 사용한 다음BigDecimal(String)생성자. " 정확한 전환으로 종종 예기치 않은 결과가 나오는 경우가 있습니다 (예 :0.1리터럴). 그러나 숫자에 대해서는 아무 것도 알지 못하기 때문에 ' 기원, 나는 그들을 다루는 유일한 합리적인 방법은 그것에 대해 아무 것도 생각하지 않는 것이라고 생각한다. - rolve

11

당신을 위해 일할 수있는 한 가지 해결책은T extends Number하지만 함께T extends Number & Comparable. 이 유형은 다음을 의미합니다. "T구현하는 유형에만 설정할 수 있습니다.양자 모두인터페이스. "

따라서 모든 비슷한 숫자로 작동하는 코드를 작성할 수 있습니다. 정적으로 타이핑되고 우아한.

이것은 BennyBoy가 제안하는 것과 같은 솔루션이지만 비교기 클래스뿐만 아니라 모든 종류의 메서드와 함께 작동합니다.

public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
    if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}

public void test() {
    compfunc(2, 1); // Works with Integer.
    compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
    compfunc(2, 1.0); // Compilation error! Different types.
    compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}


2

이것은 Number를 확장하고 자신과 비교할 수있는 모든 클래스에서 작동합니다.

class NumberComparator<T extends Number> implements Comparator<T> {

    public int compare(T a, T b){
        if (a instanceof Comparable) 
            if (a.getClass().equals(b.getClass()))
                return ((Comparable<T>)a).compareTo(b);        
        throw new UnsupportedOperationException();
    }
}


5

가장 "일반적인"Java 원시 번호는 double이므로 간단히 사용하십시오.

a.doubleValue() > b.doubleValue()

대부분의 경우 충분하지만, 숫자를 두 배로 변환 할 때 미묘한 문제가 있습니다. 예를 들어 BigInteger에서는 다음과 같이 할 수 있습니다.

    BigInteger a = new BigInteger("9999999999999992");
    BigInteger b = new BigInteger("9999999999999991");
    System.out.println(a.doubleValue() > b.doubleValue());
    System.out.println(a.doubleValue() == b.doubleValue());

결과 :

false
true

이것이 매우 극단적 인 경우라고 생각하지만 이것은 가능합니다. 아니요 - 일반적인 100 % 정확한 방법은 없습니다. Number 인터페이스는 exactValue ()와 같은 메소드가 없기 때문에 정보를 잃어 버리지 않고 숫자를 완벽하게 표현할 수있는 유형으로 변환됩니다.

실제로 완벽한 숫자를 갖는 것은 불가능합니다. 예를 들어 숫자 Pi를 나타내는 것은 유한 공간을 사용하는 산술을 사용하여 불가능합니다.


  • Double을 사용하여 완전히 표현할 수도 없기 때문에 Long에 대해서도 작동하지 않습니다. - Christopher Barber

2

if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}

노트 :그만큼instanceof확인이 반드시 필요한 것은 아닙니다. 정확히 어떻게 비교할 것인지에 달려 있습니다. 물론 당신은 물론 항상.doubleValue(), 모든 번호는 나열된 방법을 제공해야하므로이리.

편집하다: 코멘트에 명시된 바와 같이 BigDecimal과 친구들을 확인해야합니다 (항상). 그러나 그들은.compareTo()방법:

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 


  • 이건 어떤가요BigDecimal또는 복식에 대한 범위를 벗어나는 다른 유형? - b_erb
  • if (bigDecimal의 yourNumber 인스턴스 &BigDecimal의 otherNumber 인스턴스) {boolean greaterThanOtherNumber = yourNumber.compareTo (otherNumber) > 0; } - Tedil

1

당신은 단순히 사용할 수 있습니다.Number's doubleValue()그들을 비교하는 방법; 그러나 결과가 귀하의 요구에 충분히 정확하지 않을 수 있습니다.


  • 나는 이것이 너무 위험하다고 생각한다. - Yaneeve
  • BigInteger와 BigDecimal는 Number도 구현합니다. 이 방법은 효과가 없습니다. - Roman
  • 사실 그것은 위험 할 수 있으며 실패 할 수 있습니다. 앞서 언급 한 정확성 / 정확성 문제보다 더 많은주의 사항이 있습니다. 그러나 큰 BigDecimal을 처리 할 필요가 없다면 OP의 필요에 맞을 수도 있습니다. - Steven Mackenzie

1

이건 어때? 확실히 좋은 것은 아니지만 언급 된 모든 필요한 경우를 다룹니다.

public class SimpleNumberComparator implements Comparator<Number>
    {
        @Override
        public int compare(Number o1, Number o2)
        {
            if(o1 instanceof Short && o2 instanceof Short)
            {
                return ((Short) o1).compareTo((Short) o2);
            }
            else if(o1 instanceof Long && o2 instanceof Long)
            {
                return ((Long) o1).compareTo((Long) o2);
            }
            else if(o1 instanceof Integer && o2 instanceof Integer)
            {
                return ((Integer) o1).compareTo((Integer) o2);
            }
            else if(o1 instanceof Float && o2 instanceof Float)
            {
                return ((Float) o1).compareTo((Float) o2);
            }
            else if(o1 instanceof Double && o2 instanceof Double)
            {
                return ((Double) o1).compareTo((Double) o2);
            }
            else if(o1 instanceof Byte && o2 instanceof Byte)
            {
                return ((Byte) o1).compareTo((Byte) o2);
            }
            else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
            {
                return ((BigInteger) o1).compareTo((BigInteger) o2);
            }
            else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
            {
                return ((BigDecimal) o1).compareTo((BigDecimal) o2);
            }
            else
            {
                throw new RuntimeException("Ooopps!");
            }

        }

    }


  • 이 방법은 혼합 된 숫자 형식 목록에서 작동하지 않습니다. - finnw
  • 당신이 말했듯이, 좋지도 않아요. - keni

0

다음과 같은 메소드가 있다고 가정 해 보겠습니다.

public <T extends Number> T max (T a, T b) {
   ...
   //return maximum of a and b
}

정수 만있는 경우, long과 double을 매개 변수로 전달할 수 있습니다. 메소드 서명을 다음과 같이 변경할 수 있습니다.

public <T extends Number> T max(double a, double b) {
   return (T)Math.max (a, b);
}

이것은 byte, short, integer, long 및 double에 대해 작동합니다.

BigInteger 또는 BigDecimal 또는 부동 소수점 및 복식 혼합이 전달 될 수 있다고 가정하면 이러한 모든 유형의 매개 변수를 비교하는 일반적인 방법을 만들 수 없습니다.


0

Number 인스턴스가Atomic (즉 AtomicInteger) 다음과 같은 것을 할 수 있습니다 :

private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {

 Class<? extends Number> n1Class = n1.getClass();
 if (n1Class.isInstance(n2)) {
  Method compareTo = n1Class.getMethod("compareTo", n1Class);
  return (Integer) compareTo.invoke(n1, n2);
 }

 return -23;
}

이것은 모든 비 원자NumberComparable를 구현합니다.

편집하다:

이것은 반영으로 인해 값이 비쌉니다.

2 번 수정:

이것은 물론 소수점을 int 또는 일부와 비교하려고하는 경우를 취하지 않습니다 ...

3 수정:

이것은 Comparable을 구현하지 않는 Number의 사용자 정의 하위 클래스가 없다고 가정합니다 (thanks @DJClayworth)


  • 여기에는 Number의 사용자 정의 하위 항목이 없다고 가정합니다. - DJClayworth
  • @DJClayworth : 사실. 그리고 제가 상기 언급 한 유일한 제약 조건은 아닙니다. 하지만 왜 Number '의 자손으로 정의 된 자손이 있을까요? - Yaneeve
  • 사용자 정의 된 자손의 예제Number~이다.org.apache.commons.math.fraction.BigFraction이 프로그램은 많은 프로그램에서 사용합니다. - finnw

연결된 질문


관련된 질문

최근 질문