Java: Ціле число дорівнює = ==


152

Що стосується Java 1.5, ви можете в значній мірі взаємодіяти Integerз intбагатьма ситуаціями.

Однак я знайшов потенційний дефект у своєму коді, який мене трохи здивував.

Наступний код:

Integer cdiCt = ...;
Integer cdsCt = ...;
...
if (cdiCt != null && cdsCt != null && cdiCt != cdsCt)
    mismatch = true;

виявилося невірно встановлення невідповідності, коли значення були рівні, хоча я не можу визначити, за яких обставин. Я встановив точку розриву в Eclipse і побачив, що Integerзначення є обома 137, і я перевірив булевий вираз, і він сказав, що це помилково, але коли я переступив це, він встановлював невідповідність істинному.

Зміна умовного на:

if (cdiCt != null && cdsCt != null && !cdiCt.equals(cdsCt))

виправили проблему.

Чи може хтось пролити світло на те, чому це сталося? Поки що я бачив лише поведінку свого локального господаря на своєму ПК. У цьому конкретному випадку код успішно пройшов приблизно 20 порівнянь, але не вдався до 2. Проблема була постійно відтворюваною.

Якщо це поширена проблема, вона повинна викликати помилки в інших наших середовищах (розробник і тест), але поки що ніхто не повідомив про проблему після сотень тестів, що виконують цей фрагмент коду.

Чи все ж не є законним використовувати ==для порівняння двох Integerзначень?

На додаток до всіх тонких відповідей нижче, наступне посилання stackoverflow містить ще трохи додаткової інформації. Насправді він би відповів на моє первісне запитання, але оскільки я не згадав про автобоксинг у своєму питанні, він не відображався у вибраних пропозиціях:

Чому компілятор / JVM не може зробити автобоксинг «просто діючим»?

Відповіді:


238

JVM кешує цілісні значення. == працює лише для чисел від -128 до 127 http://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


1
Дякую, це безумовно пояснює, чому 137 не вдається! І це також відповідає на моє запитання, чому це не є поширеною проблемою, у 95% випадків, з якими я зіткнувся, значення було б менше 127. Добре зараз це зрозуміти, хоча для тих 5%, де його немає.
Джеремі Гудделл

1
Цікава сторона: до пари тижнів тому cdiCt та cdsCt були обидва ints, тому це було добре, але я повинен був зробити їх Integers, щоб перевірити наявність нульової ситуації, яка розглядається інакше ...
Jeremy Goodell,

3
@Jeremy Так, це досить неясна проблема, але, як правило, ви використовуєте .equals () для об'єктів і == для примітивів. Ви не можете розраховувати на автобокс для тестування рівності.
Адам

1
Lol, галочка повернемо тоді до тебе! Схоже, у Коліна все одно є більш ніж достатньо балів.
Джеремі Гудделл

2
Зауважте, що новий Integer (1)! = Новий Integer (1) також. новий ЗАВЖДИ повертає нову адресу. Автобоксинг використовує кешовану версію. Інші способи, які повертають цілі особи (не додаючи їх), ймовірно, також повертають кешоване значення.
Білл К

77

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

Існує хитрість, коли Integerвід -128 до 127 посилання будуть такими ж, як і для автоматичного використання, Integer.valueOf()який кешує невеликі цілі числа.

Якщо значення p, яке міститься у вікні, є істинним, хибним, байтом, знаком в діапазоні \ u0000 до \ u007f або int або коротким числом від -128 до 127, то нехай r1 і r2 є результатами будь-яких двох перетворень боксу з р. Це завжди так, що r1 == r2.


Ресурси:

На цю ж тему:


1
Це гарантія від JLS чи просто для JVM Oracle?
Thorbjørn Ravn Andersen

Процитувана частина - від JLS, тому це гарантія від JLS
Колін Геберт

Re: гарантія. Я все ще не надто покладався на це. new Integer(1) == new Integer(1)все ще помилково.
Тіло

@Thilo new ... == new ...завжди false.
MC Імператор

2
@Thilo Правда, завжди використовуйте equals()при роботі з предметами. Це має бути одним із перших речей, які слід знати, вивчаючи Java. До речі, я б здогадався, що конструктор Integerбув приватним, тобто екземпляри завжди створювалися за допомогою valueOf()методу. Але я бачу, що конструктор публічний.
MC Імператор

5

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


Хороша відповідь, але це не пояснює, чому це не вдається лише 137.
Джеремі Гуделл

4

Integerпосилається на посилання, тобто при порівнянні посилань, які ви порівнюєте, якщо вони вказують на один і той же об'єкт, а не значення. Отже, проблема, яку ви бачите. Причина, з якою вона так добре працює з простими intтипами, полягає в тому, що вона розблоковує значення, що міститься в Integer.

Чи можу додати, що якщо ви робите те, що ви робите, з чого ifпочинати заяву?

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );

4

"==" завжди порівнюють значення пам'яті або посилання на об'єкти значень. метод рівних завжди порівнюють значення. Але рівне також опосередковано використовує оператор "==" для порівняння значень.

Integer використовує кеш Integer для зберігання значень від -128 до +127. Якщо оператор == використовується для перевірки значень від -128 до 127, то він повертає значення true. для інших, крім цих значень, воно повертається помилковим.

Перейдіть за посиланням для отримання додаткової інформації


1

Для правильності використання ==ви можете просто розв’язати одне зі порівняних Integerзначень перед ==порівнянням, наприклад:

if ( firstInteger.intValue() == secondInteger ) {..

Другий буде автоматично нерозподіленим (звичайно, ви повинні перевірити це на nullперший).


0

Окрім цих наведених чудових відповідей, я дізнався, що:

НІКОЛИ не порівнюйте об’єкти з ==, якщо ви не збираєтесь порівнювати їх за посиланнями.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.