Как правильно сравнить два целых числа в Java?

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

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)

?

Ответы

Ответ 1

Нет, == между Integer, Long и т.д. Будет проверять равенство ссылок - т.е.

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

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

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

Так

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). Он по-прежнему ссылается на равенство, но ссылки действительно равны.

Если упакованное значение p является целочисленным литералом типа int между -128 и 127 включительно (§3.10.1), или логический литерал true или false (§3.10.3), или символьный литерал между '\ u0000' и '\ u007f' включительно (§3.10.4), тогда пусть a и b будут результатами любых двух боксов преобразования р. Это всегда так, что a == b.

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

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

или

if (x.equals(y))

Как вы говорите, для любого сравнения между типом оболочки (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, тогда целочисленное сравнение со знаком выполнено; если этот повышенный тип с плавающей или двойной, затем с плавающей точкой сравнение выполняется.

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

Ответ 2

== все равно будет проверять равномерность объекта. Однако легко обмануть:

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). Это хорошая оптимизация, поскольку примитивные классы ящиков неизменяемы.

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

Вызов

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' может быть null, так как это объект. Сравнение таким образом не приведет к исключению нулевого указателя.

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

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

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

Уже упоминалось, что сравнение == для 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.

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

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

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

Ответ 7

В моем случае мне пришлось сравнивать два 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);
}

Ответ 8

Поскольку метод сравнения должен выполняться на основе типа int (x == y) или класса Integer (x.equals(y)) с правым оператором

public class Example {

    public static void main(String[] args) {
     int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1]!=arr[j]) && (arr[j]!=arr[j+1])) 
                System.out.println("int>"+arr[j]);


    Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1]))) 
                System.out.println("Interger>"+I_arr[j]);
    }
}

Ответ 9

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

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