Сравнивая значения двух общих чисел

Я хочу сравнить с переменными, как типа T extends Number. Теперь я хочу знать, какая из двух переменных больше другой или равна. К сожалению, я еще не знаю точного типа, я знаю только, что это будет подтип java.lang.Number. Как я могу это сделать?

РЕДАКТИРОВАТЬ: я попробовал другое обходное решение, используя TreeSet s, который фактически работал с естественным упорядочением (конечно, он работает, все подклассы Number реализуют Comparable, за исключением AtomicInteger и AtomicLong), Таким образом, я потеряю повторяющиеся значения. При использовании List s Collection.sort() не будет принимать мой список из-за связанных несоответствий. Очень неудовлетворительно.

Ответ 1

Рабочее (но хрупкое) решение выглядит примерно так:

class NumberComparator implements Comparator<Number> {

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

}

Тем не менее, он не очень хорош, поскольку рассчитывает на то, что toString вернет значение, доступное для анализа BigDecimal (что делают стандартные классы Java Number, но чего не требует контракт Number).

Отредактируйте, семь лет спустя: Как указано в комментариях, есть (по крайней мере?) Три особых случая toString, которые вы должны принять во внимание:

Ответ 2

Это должно работать для всех классов, которые расширяют число и сравнимы с самим собой. Добавляя и сравнивая, вы разрешаете удалять все проверки типов и предоставляете проверки типа времени выполнения и бросание ошибок бесплатно по сравнению с ответом Сармуна.

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

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

Ответ 3

Одно из решений, которое может работать для вас, - это работать не с 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
}

Ответ 4

После запроса подобного вопроса и изучения ответов здесь я придумал следующее. Я думаю, что он эффективнее и надежнее, чем решение 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);
    }
}

Ответ 5

Самый "общий" примитивный номер Java - двойной, поэтому просто

a.doubleValue() > b.doubleValue()

должно быть достаточно в большинстве случаев, но... здесь есть тонкие проблемы при преобразовании чисел в double. Например, с 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% -ного точного способа. В числовом интерфейсе нет метода, например exactValue(), который преобразуется в некоторый тип, способный представлять число совершенным образом без потери информации.

На самом деле таких совершенных чисел вообще невозможно - например, число Pi невозможно с использованием любой арифметики с использованием конечного пространства.

Ответ 6

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

Ответ 7

Это должно работать для всех классов, которые расширяют число и сравниваются с самими собой.

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

Ответ 8

Вы можете просто использовать метод Number doubleValue() для их сравнения; однако вы можете обнаружить, что результаты недостаточно точны для ваших нужд.

Ответ 9

Как насчет этого? Определенно не приятно, но речь идет обо всех упомянутых случаях.

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!");
            }

        }

    }

Ответ 10

Предположим, что у вас есть метод:

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

Если вы знаете, что в качестве параметров могут быть указаны только целые числа, длинные и удвоенные значения, вы можете сменить подпись метода на:

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

Это будет работать для байтов, коротких, целых, длинных и двойных.

Если вы предполагаете, что BigInteger или BigDecimal или сочетание поплавков и удвоений могут быть переданы, вы не можете создать один общий метод для сравнения всех этих типов параметров.

Ответ 11

Если ваши экземпляры 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;
}

Это потому, что все неатомные Number реализуют Comparable

ИЗМЕНИТЬ

Это дорого из-за отражения: я знаю

РЕДАКТИРОВАТЬ 2:

Это, конечно, не принимает случай, в котором вы хотите сравнить десятичные числа с ints или некоторые такие...

РЕДАКТИРОВАТЬ 3:

Это предполагает, что не существует пользовательских потомков Number, которые не реализуют Comparable (спасибо @DJClayworth)

Ответ 12

System.out.println(new BigDecimal(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1f).toPlainString());
System.out.println(Float.valueOf(0.1f).toString());
System.out.println(Float.valueOf(0.1f).doubleValue());