Сравнение чисел в Java

В Java все числовые типы распространяются от java.lang.Number. Было бы неплохо иметь такой метод, как:

public boolean areEqual(Number first, Number second) {
    if (first != null && second != null) {
        return first.equals(second);
    }
}

Меня беспокоят случаи, когда double 2.00000 не равен int 2. Выполняются ли они встроенными равными? Если нет, есть ли способ написать функцию сравнения простых чисел в java? (внешние библиотеки, такие как apache commons в порядке)

Ответ 1

A Double НИКОГДА equals к Integer. Более того, a Double не совпадает с Double.

Java имеет примитивные типы и ссылочные типы. Поистине числовые типы в Java не распространяются от Number, потому что они являются примитивами.

Возможно, вам захочется рассмотреть систему, в которой вы не смешиваете типы, поскольку это обычно вызывает много проблем с неявными/явными преобразованиями, которые могут/не могут потерять информацию и т.д.

Связанные вопросы

Вкл int vs Integer:

В Number сравнение:

См. также


При вычислении смешанного типа

Вычисление смешанного типа является предметом, по меньшей мере, 4 головоломок в Java Puzzlers.

Вот несколько выдержек:

как правило, лучше избегать вычислений смешанного типа [...], потому что они по сути запутывают [...] Нигде это более очевидно, чем в условных выражениях. Смешанные сравнения типов всегда сбивают с толку, потому что система вынуждена продвигать один операнд в соответствии с типом другого. Преобразование невидимо и может не дать ожидаемых результатов

Рецепт. Избегайте вычислений, объединяющих интегральные и с плавающей точкой. Предпочитайте интегральную арифметику с плавающей точкой.

Ответ 2

Я знаю его старую тему, но... Чтобы сравнить два числа в Java, вы можете использовать метод compareTo из BigDecimal. BigDecimal может содержать все от короткого до двойного или BigInteger, поэтому для него это идеальный класс.

Итак, вы можете попытаться написать что-то вроде этого:

public int compareTo(Number n1, Number n2) {
    // ignoring null handling
    BigDecimal b1 = new BigDecimal(n1.doubleValue());
    BigDecimal b2 = new BigDecimal(n2.doubleValue());
    return b1.compareTo(b2);
}

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

assertTrue(compareTo(new Integer(1), new Integer(2)) == -1);
assertTrue(compareTo(new Integer(1), new Double(2.0)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MAX_VALUE)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MIN_VALUE)) == 1);
assertTrue(compareTo(new Integer(1), new Double(1.000001)) == -1);
assertTrue(compareTo(new Integer(1), new Double(1.000)) == 0);
assertTrue(compareTo(new Integer(1), new Double(0.25*4)) == 0);
assertTrue(compareTo(new Integer(1), new AtomicLong(1)) == 0);

Ответ 3

Конкретный метод, который вы предлагаете, потерпит неудачу, потому что он использует equals(), унаследованный от Object. То есть, он будет проверять, были ли теги Number одинаковыми, а не были ли эти значения одинаковыми.

Если бы это был просто иллюстративный пример, я обновил свой ответ.

Полигенный ответ на самом деле в значительной степени охватывает землю, к которой я направлялся. Вы также можете быть заинтересованы в этом вопросе: Почему java.lang.Number не реализует Comparable?.

Ответ 4

Если вы хотите знать, одинаковы ли ссылки на объекты, то существующие методы соответствуют счету. A Double, представляющий 2.0 и Integer, представляющие 2, безусловно, разные объекты и, конечно, не взаимозаменяемы в общем смысле.

Если вы просто хотите знать, совпадают ли числовые значения, вы можете использовать метод Number.doubleValue() для преобразования как числа удваиваются, затем сравнивайте эти числа вместе (вероятно, учитывая небольшой допуск, так как большинство чисел представлены неточно, например 1.99999999996583, для чего должно быть 2, в зависимости от промежуточных этапов расчета). Что-то вроде следующего:

private static final double EPSILON = 0.000000000000001d;    

public static boolean areEquivalentNumbers(Number a, Number b)
{
   if (a == null)
   {
      return b == null;
   }
   else if (b == null)
   {
      return false;
   }
   else
   {
      return Math.abs(a.doubleValue() - b.doubleValue()) < EPSILON;
   }
}

Ответ 5

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

public boolean areEqual(Number first, Number second) {
    if (first == null) {
        return second == null;
    }
    if (second == null) {
        return false;
    }

    return first.toString().equals(second.toString());
}

Ответ 6

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

boolean compare(Object o1, Object o2)
{
  if (o1==null)
    return o2==null;
  if (o2==null)
    return false;
  return o1.equals(o2);
}

Это гораздо более кратким, и я считаю немного более эффективным, чтобы написать:

boolean compare(Object o1, Object o2)
{
  return o1==o2 || o1!=null && o2!=null && o1.equals(o2);
}

Если оба значения равны нулю, o1 == o2 вернет true. Если это не так, но они - один и тот же объект, это тоже хорошо.

Технически o2!= null не требуется для большинства реализаций равных, но если вы действительно были настолько универсальны, чтобы делать это на объектах, как в приведенном выше примере, вы, конечно, не знаете, как было написано каждое переопределение.

Ответ 7

вы не можете позвонить

number.equals(number2);

потому что, если число равно Double и number2 является Integer, они не будут одного класса, и вы получите исключение, сообщающее вам об этом факте.

Вы можете написать класс сравнения самостоятельно, который принимает объекты Number, но вам нужно будет учитывать различные подклассы Number