Как правильно сравнить два целых числа в 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)

?

8 ответов

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

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

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

или же

if (x.equals(y))

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

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

if (x.compareTo(y) < 0)

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

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

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

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

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

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

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

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

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

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

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

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

Мы всегда должны использовать метод equals() для сравнения двух целых чисел - это рекомендуемая практика.

Если мы сравним два целых числа с помощью ==, это будет работать для определенного диапазона целочисленных значений (Integer от -128 до 127) из-за внутренней оптимизации JVM.

См. Примеры:

Случай 1:

Целое число a = 100; Целое число b = 100;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

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

Случай 2: В этом случае a и b не равны, потому что он не входит в диапазон от -128 до 127.

Целое число a = 220; Целое число b = 220;

if (a == b) {
    System.out.println("a and b are equal");
} else {
   System.out.println("a and b are not equal");
}

Надлежащим образом:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true

Надеюсь, это поможет.

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

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

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

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

Призвание

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 секунд с использованием второго метода. Вам нужно будет сравнить миллиарды значений, чтобы увидеть разницу.

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

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

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

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

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

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

Поскольку метод сравнения должен выполняться на основе типа 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]);
    }
}
Другие вопросы по тегам