Як правильно порівняти два цілих числа на Java?


217

Я знаю, що якщо порівнювати коробчатий примітивний 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)

?


16
Ну, що сталося, коли ви спробували? Що ви спостерігали?
Барт Кіерс

31
@Bart Kiers: Явний експеримент міг лише спростувати, а не довести, що відбувається розпакування. Якщо використання ==замість цього equalsдає правильний результат, це може бути тому, що коробчасті номери інтерніруються або використовуються іншим способом (імовірно, оптимізація компілятора). Причина задавати це питання - з’ясувати, що відбувається всередині, а не те, що, здається, відбувається. (Принаймні, тому я тут.)
Джим Піварський

Що сталося з вашим рахунком?

Відповіді:


303

Ні, == між 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через правила боксу ( розділ 5.1.7 JLS ). Це ще посилання рівність використовується, але посилання дійсно є рівними.

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

Особисто я б користувався:

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

або

if (x.equals(y))

Як ви говорите, для будь-якого порівняння типу обгортки ( Integer, і Longт.д.) і числовий типу ( int, і longт.д.) значення типу обгортки розпаковане і тест застосовується до примітивних залученим значенням.

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

Якщо операнди оператора рівності мають чисельний тип, або один має числовий тип, а другий перетворюється (§5.1.8) в числовий тип, двійкове числове просування здійснюється на операндах (§5.6.2).

і <, <=, >і >=( ПСБ 15.20.1 )

Тип кожного з операндів оператора чисельного порівняння повинен бути типом, який може бути конвертованим (§5.1.8) у примітивний числовий тип, або виникає помилка часу компіляції. Двійкове числове просування здійснюється на операндах (§5.6.2). Якщо висунутий тип операндів є int або long, то виконується підписане ціле порівняння; якщо цей рекламований тип є плаваючою або подвійною, то проводиться порівняння з плаваючою комою.

Зверніть увагу, як нічого з цього не розглядається як частина ситуації, коли жоден тип не є числовим типом.


2
Чи є якась причина, чому можна було б написати x.compareTo(y) < 0замість цього x < y?
Макс Нанасі

1
@MaxNanasy: Не те, що я можу відразу придумати.
Джон Скіт

2
Як і в Java 1.6.27+, в класі Integer є перевантаження рівних, тому воно повинно бути таким же ефективним, як і виклик .intValue (). Він порівнює значення як примітивний int.
otterslide

Як сказав @otterslide, це більше не потрібно в Java 8. Порівняння Integer з Integer за значенням за замовчуванням.
Аксель Прието

1
@Axel: Додавання перевантаження не змінило б поведінку оператора ==, чи не так? Зараз я не в змозі протестувати, але був би дуже здивований, якби це змінилося.
Джон Скіт

44

==все одно перевірятиметься рівність об'єктів. Однак, легко обдурити:

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 ()? Крім того, чи вважаєте ви, що я повинен залишити нерівності такими, якими вони є, чи зробити це також іншим способом?

Є кілька неочевидних крайових випадків із автобоксингом. У мене IDE (Eclipse) встановлений, щоб забарвити все, що не має коробки, в червоний колір, це врятувало мене від помилок в декількох випадках. Якщо ви порівнюєте два цілих числа, використовуйте .equals, якщо ви хочете, щоб ваші нерівності були зрозумілими, напишіть групу чітко: if ((int) c <(int) d) ...; Ви також можете зробити: c.compareTo (d) <0 // === c <d
Адам Льюїс

12
І якщо змінити літеральну цифру на 200, обидва тести будуть надруковані false.
Даніель Вурвікер

2
... у більшості реалізацій JVM, тобто. Відповідно до мовної специфікації, результат може відрізнятися між реалізаціями.
Даніель Ервікер

4
Я думаю, що зрозуміліше назвати це "еталонною рівністю" - так очевидно, що ви маєте на увазі. Я звичайно розумію "рівність об'єкта", щоб означати "результат equalsвиклику".
Джон Скіт

28

З Java 1.7 ви можете використовувати Object.equals :

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

Повертає істину, якщо аргументи рівні між собою та хибні в іншому випадку. Отже, якщо обидва аргументи є нульовими, повертається true, а якщо точно один аргумент є нульовим, повертається false. В іншому випадку рівність визначається за допомогою методу рівних першого аргументу.


Це обробляє нулі, тому робить його простим. Дякую!
Даррен Паркер

10

== перевіряє рівність еталону, однак при написанні коду типу:

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

1
tl; dr -1 для рукоділля; stackoverflow.com/questions/15052216/… stackoverflow.com/questions/20897020/… stackoverflow.com/questions/3131136/integers-caching-in-java та ін., детально поясніть згадане вами питання; краще прочитати документи (або джерело lib), ніж створювати псевдотести з ризиком високої локальності результатів - не тільки ви повністю забули про нижню межу кешу (тобто -128 за замовчуванням), не тільки у вас є окремо (макс. 127, а не 128),

але ви абсолютно не гарантуєте отримання однакового результату на будь-якій машині - оскільки ви можете легко збільшити розмір кешу самостійно, YMMV. Також питанням ОП було те, як правильно порівнювати два цілі особи - ви взагалі не відповіли на це .

Я поважаю вашу думку та сприйняття тут. Я думаю, у нас просто принципово різні підходи до КС.
Cory Kendall

1
мова не про думку чи сприйняття - це про факти , які ви щиро пропускали. Проведення псевдотесту нічого не підтверджує, без будь-яких жорстких даних про резервну копію (документи, джерела тощо) та без відповіді на запитання ОП не заслуговує на те, щоб називатися ні добрими запитаннями, ні КС. Щодо "іншого підходу" - CS за визначенням є наукою ; те, що ви зробили наукою, не є ; це оманливі дрібниці (або це буде інтригуючий коментар , якщо викладено правильно) - якщо ви хочете, щоб це було наукою , виправте основні недоліки у своїй відповіді або розкрийте їх розумно , як це "працює.

Звичайно, я спробую усунути недоліки. Я не забував про нижню межу, не відчував, що це цікаво, і вирішив не включати її. Я не вірю, що я вимкнувся однією помилкою, я заявив, що java (яку я пояснив на своїй машині, в моїй ситуації) перестала оптимізувати це, що становить 128. Якби я вказав максимальне значення, це було це для, ніж ви праві, відповідь була б 127.
Cory Kendall

8

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()це лише метод getter (головний кандидат для оптимізації).

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

Моє голосування відповідає i1 == +i2& i1 > i2стилю для Integerоб'єктів, як із продуктивності, так і з послідовністю. Це також робить код переносним для примітивів, не змінюючи нічого, крім декларації типу. Використання названих методів здається мені введенням смислового шуму, подібного до сильно критикованого bigInt.add(10).multiply(-3)стилю.


Чи можете ви пояснити, що означає + у методі 4? Я спробував google це, але я отримав лише звичайні звичаї цього символу (додавання, конкатенація).
Алекс Лі

1
@AlexLi це означає саме те , що я написав - unary +(унарний плюс), дивись , наприклад , stackoverflow.com/questions/2624410 / ...

8

Дзвінок

if (a == b)

Працюватиме більшу частину часу, але не гарантовано завжди працювати, тому не використовуйте це.

Найбільш правильним способом порівняння двох класів цілих чисел для рівності, припускаючи, що вони названі '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");
}

1
if (a==b)працює лише для малих значень і не працюватиме більшу частину часу.
Тоні

Він працює до 127, оскільки це кеш-пам'ять Integer за замовчуванням, яка гарантує, що всі числа до 127 мають однакове опорне значення. Ви можете встановити кеш-пам'ять вище 127, якщо хочете, але просто не використовуйте == для безпеки.
otterslide

2

Ми завжди повинні використовувати метод equals () для порівняння для двох цілих чисел. Це рекомендована практика.

Якщо ми порівняємо два цілих числа, використовуючи ==, які працювали б для певного діапазону цілих значень (Ціле число від -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

Я сподіваюся, що це допомагає.


1

У моєму випадку я повинен був порівняти два 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);
}

-1

Оскільки метод порівняння потрібно проводити на основі типу 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]);
    }
}

-2

цей метод порівнює два цілих числа з нульовою перевіркою, див. тести

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

4
Для цього я думаю, що було б краще використовувати Objects.equals(x,y)метод замість того, щоб прокати свій.
ryvantage
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.