Как правильно сравнить два целых числа в Java? - PullRequest
185 голосов
/ 04 октября 2009

Я знаю, что если вы сравните упакованное примитивное целое число с константой, такой как:

Integer a = 4;
if (a < 5)

a автоматически распаковывается, и сравнение будет работать.

Однако, что происходит, когда вы сравниваете два блока Integers и хотите сравнить либо равенство, либо меньше / больше, чем?

Integer a = 4;
Integer b = 5;

if (a == b)

Приведенный выше код приведет к проверке того, являются ли они одним и тем же объектом, или в этом случае он будет автоматически отключен?

Как насчет:

Integer a = 4;
Integer b = 5;

if (a < b)

Ответы [ 8 ]

260 голосов
/ 04 октября 2009

Нет, == между Integer, Long и т. Д. Проверит на равенство ссылок - т.е.

Integer x = ...;
Integer y = ...;

System.out.println(x == y);

это проверит, относятся ли x и y к одному и тому же объекту , а не равно объектам.

So

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);

гарантированно печатает false. Стаж «маленьких» значений в автобоксах может привести к сложным результатам:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

Будет напечатано true, в соответствии с правилами бокса ( JLS раздел 5.1.7 ). Это все еще используется равенство ссылок, но ссылки действительно равны равны.

Лично я бы использовал:

if (x.intValue() == y.intValue())

или

if (x.equals(y))

Последний немного менее эффективен - для Integer.equals(Integer) нет перегрузки, поэтому он должен будет выполнять проверку типа времени выполнения, тогда как первый использует тот факт, что мы уже знаем, что оба объекта Integer s.

К счастью, compareTo знает о типах, поэтому:

if (x.compareTo(y) < 0)

все еще должен быть эффективным. Конечно, это территория микрооптимизации, и вы должны использовать код, который вы найдете наиболее понятным - после того, как убедитесь, что он правильный:)

Как вы говорите, для любого сравнения между типом оболочки (Integer, Long и т. Д.) И числовым типом (int, long и т. Д.) Значение типа оболочки составляет без упаковки и тест применяется к примитивным значениям.

Это происходит как часть двоичного числового продвижения ( JLS, раздел 5.6.2 ). Посмотрите на документацию каждого отдельного оператора, чтобы увидеть, применяется ли она. Например, из документов для == и != ( JLS 15.21.1 ):

Если операнды равенства оператор оба имеют числовой тип, или один числового типа, а другой конвертируемое (§5.1.8) в числовое тип, двоичное числовое продвижение выполняется на операндах (§5.6.2).

и для <, <=, > и >= ( JLS 15.20.1 )

Тип каждого из операндов оператор числового сравнения должен быть тип, который может быть преобразован (§5.1.8) в примитивный числовой тип или происходит ошибка времени компиляции. двоичный численное продвижение проводится на операнды (§5.6.2). Если повышен тип операндов - int или long, тогда целочисленное сравнение со знаком выполнила; если этот повышенный тип с плавающей или двойной, затем с плавающей точкой сравнение выполнено.

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

41 голосов
/ 04 октября 2009

== все еще будет проверять равенство объектов. Легко быть обманутым, однако:

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

Ваши примеры с неравенствами будут работать, поскольку они не определены для объектов. Однако при сравнении == равенство объектов все равно будет проверяться. В этом случае, когда вы инициализируете объекты из коробочного примитива, используется один и тот же объект (как для a, так и для b). Это хорошая оптимизация, поскольку классы примитивных блоков неизменны.

14 голосов
/ 22 марта 2018

Начиная с Java 1.7 вы можете использовать Objects.equals :

java.util.Objects.equals(oneInteger, anotherInteger);

Возвращает true, если аргументы равны друг другу и false иначе. Следовательно, если оба аргумента равны нулю, возвращается true и если ровно один аргумент равен нулю, возвращается false. Иначе, равенство определяется с помощью метода равенства первого аргумент.

10 голосов
/ 16 июля 2015

== проверяет равенство ссылок, однако при написании кода вроде:

Integer a = 1;
Integer b = 1;

Java достаточно умен, чтобы использовать один и тот же неизменяемый для a и b, так что это правда: a == b Любопытно, я написал небольшой пример, чтобы показать, где Java прекращает оптимизацию таким образом:

public class BoxingLol {
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            Integer a = i;
            Integer b = i;
            if (a != b) {
                System.out.println("Done: " + i);
                System.exit(0);
            }
        }
        System.out.println("Done, all values equal");
    }
}

Когда я компилирую и запускаю это (на моей машине), я получаю:

Done: 128
8 голосов
/ 22 сентября 2016

Calling

if (a == b)

Будет работать большую часть времени, но это не всегда гарантирует работу, поэтому не используйте его.

Самый правильный способ сравнить два класса Integer на равенство, предполагая, что они названы 'a' и 'b', это вызвать:

if(a != null && a.equals(b)) {
  System.out.println("They are equal");
}

Вы также можете использовать этот способ, который немного быстрее.

   if(a != null && b != null && (a.intValue() == b.intValue())) {
      System.out.println("They are equal");
    } 

На моей машине 99 миллиардов операций заняли 47 секунд, используя первый метод, и 46 секунд, используя второй. Вам нужно сравнивать миллиарды значений, чтобы увидеть разницу.

Обратите внимание, что 'a' может быть нулевым, поскольку это объект. Сравнение таким способом не вызовет исключение нулевого указателя.

Для сравнения больше и меньше, используйте

if (a != null && b!=null) {
    int compareValue = a.compareTo(b);
    if (compareValue > 0) {
        System.out.println("a is greater than b");
    } else if (compareValue < 0) {
        System.out.println("b is greater than a");
    } else {
            System.out.println("a and b are equal");
    }
} else {
    System.out.println("a or b is null, cannot compare");
}
6 голосов
/ 23 мая 2015

tl; dr Мое мнение состоит в том, чтобы использовать унарный + для запуска распаковки одного из операндов при проверке на равенство значений и просто использовать математические операторы в противном случае. Обоснование следует:

Уже упоминалось, что == сравнение для Integer - это сравнение идентичности, которое обычно не является тем, чего хочет программист, и что цель состоит в сравнении значений; тем не менее, я немного изучил науку о том, как сделать это сравнение наиболее эффективно, с точки зрения компактности, правильности и скорости кода.

Я использовал обычный набор методов:

public boolean method1() {
    Integer i1 = 7, i2 = 5;
    return i1.equals( i2 );
}

public boolean method2() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2.intValue();
}

public boolean method3() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2;
}

public boolean method4() {
    Integer i1 = 7, i2 = 5;
    return i1 == +i2;
}

public boolean method5() { // obviously not what we want..
    Integer i1 = 7, i2 = 5;
    return i1 == i2;
}

и получил этот код после компиляции и декомпиляции:

public boolean method1() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    return var1.equals( var2 );
}

public boolean method2() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method3() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method4() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method5() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2 == var1 ) {
        return true;
    } else {
        return false;
    }
}

Как вы можете легко увидеть, метод 1 вызывает Integer.equals() (очевидно), методы 2-4 приводят к точно такому же коду , разворачивая значения с помощью .intValue() и затем сравнивая их напрямую и метод 5 просто запускает сравнение идентификаторов, что является неправильным способом сравнения значений.

Поскольку (как уже упоминалось, например, в JS) equals() влечет за собой накладные расходы (он должен выполнять instanceof и неконтролируемое приведение), методы 2-4 будут работать с точно такой же скоростью, заметно лучше, чем метод 1, когда используется в тесных циклах, поскольку HotSpot вряд ли оптимизирует приведение & instanceof.

Это очень похоже на другие операторы сравнения (например, < / >) - они будут запускать распаковку, в то время как использование compareTo() не будет - но на этот раз операция с высокой степенью оптимизации HS, так как intValue() это просто метод получения (главный кандидат на оптимизацию).

На мой взгляд, редко используемая версия 4 является наиболее кратким способом - каждый опытный разработчик C / Java знает, что унарный плюс в большинстве случаев равен приведению к int / .intValue() - хотя это может быть немного WTF момент для некоторых (в основном тех, кто не использовал унарный плюс в своей жизни), это, вероятно, показывает намерение наиболее четко и кратко - это показывает, что мы хотим, чтобы значение int одного из операнды, заставляя другое значение также распаковать. Также неоспоримо наиболее похоже на обычное i1 == i2 сравнение используется для примитивного int значения.

Мой голос идет за стиль i1 == +i2 & i1 > i2 для Integer объектов, как по соображениям производительности, так и по соображениям согласованности. Это также делает код переносимым на примитивы, не изменяя ничего, кроме объявления типа. Использование именованных методов похоже на создание семантического шума, похожего на критикуемый стиль bigInt.add(10).multiply(-3).

1 голос
/ 21 сентября 2018

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

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

//considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}
0 голосов
/ 02 марта 2017

этот метод сравнивает два целых числа с нулевой проверкой, см. Тесты

public static boolean compare(Integer int1, Integer int2) {
    if(int1!=null) {
        return int1.equals(int2);
    } else {
        return int2==null;
    }
    //inline version:
    //return (int1!=null) ? int1.equals(int2) : int2==null;
}

//results:
System.out.println(compare(1,1));           //true
System.out.println(compare(0,1));           //false
System.out.println(compare(1,0));           //false
System.out.println(compare(null,0));        //false
System.out.println(compare(0,null));        //false
System.out.println(compare(null,null));     //true
...