Java: целое число равно против ==


153

Начиная с Java 1.5, вы можете в значительной степени обмениваться Integer с intво многих ситуациях.

Тем не менее, я обнаружил потенциальный дефект в моем коде, который немного удивил меня.

Следующий код:

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

оказалось, что неправильно установить несоответствие, когда значения были равны, хотя я не могу определить, при каких обстоятельствах. Я установил точку останова в Eclipse и увидел, чтоInteger оба значения были равны 137, и я проверил логическое выражение, и оно сказало, что оно ложно, но когда я перешагнул через него, было установлено несоответствие равным true.

Изменение условного на:

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

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

Может кто-нибудь пролить свет на то, почему это произошло? До сих пор я видел только поведение моего локального хоста на своем ПК. В этом конкретном случае код успешно прошел около 20 сравнений, но не удался на 2. Проблема была постоянно воспроизводимой.

Если это распространенная проблема, она должна вызывать ошибки в других наших средах (dev и test), но до сих пор никто не сообщал о проблеме после сотен тестов, выполняющих этот фрагмент кода.

Разве это не законно использовать ==для сравнения двух 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 были обоими типами int, так что это было нормально, но мне приходилось делать их целочисленными, чтобы проверять нулевую ситуацию, которая обрабатывается по-разному ...
Джереми Гуделл

3
@ Джереми Да, это довольно непонятная проблема, но, как правило, вы используете .equals () для объектов и == для примитивов. Вы не можете полагаться на автоматическую коробку для тестирования на равенство.
Адам

1
Lol, галочка обратно к тебе тогда! Похоже, у Колина уже достаточно очков.
Джереми Гуделл

2
Обратите внимание, что new Integer (1)! = New Integer (1) также. new ВСЕГДА возвращает новый адрес. Автобокс использует кэшированную версию. Другие способы, которые возвращают целые числа (не обновляя их), вероятно, также возвращают кэшированное значение.
Билл К

77

Вы не можете сравнить два Integerс простыми ==объектами, так что большинство ссылок времени не будут одинаковыми.

Есть хитрость, в Integerдиапазоне от -128 до 127, ссылки будут такими же, как в автобоксах, Integer.valueOf()которые кэшируют маленькие целые числа.

Если значение p в штучной упаковке является истинным, ложным, байтом, символом в диапазоне от \ u0000 до \ u007f или целым или коротким числом от -128 до 127, то пусть r1 и r2 будут результатами любых двух преобразований бокса из р. Это всегда тот случай, когда r1 == r2.


Ресурсы :

На ту же тему:


1
Это гарантия от JLS или только для Oracle JVM?
Турбьёрн Равн Андерсен

Цитируемая часть от JLS, так что это гарантия от JLS
Колин Хеберт

Re: гарантия. Я бы все еще не полагался на это слишком сильно. new Integer(1) == new Integer(1)все еще ложь.
Тило

@Тило new ... == new ...всегда false.
MC Emperor

2
@Thilo Правда, всегда используйте equals()при работе с объектами. Это должно быть одной из первых вещей, которые нужно знать при изучении Java. Кстати, я бы предположил, что конструктор Integerбыл закрытым, то есть экземпляры всегда создавались с помощью valueOf()метода. Но я вижу, что конструктор является публичным.
MC Emperor

5

Проблема в том, что ваши два объекта Integer - это просто объекты. Они не совпадают, потому что вы сравниваете две ссылки на объекты, а не значения внутри. Очевидно .equals, переопределяется для обеспечения сравнения значений, а не сравнения ссылок на объекты.


Хороший ответ, но он не объясняет, почему он терпит неудачу только для 137.
Джереми Гуделл

4

Integerотносится к ссылке, то есть при сравнении ссылок, которые вы сравниваете, если они указывают на один и тот же объект, а не на значение. Следовательно, проблема, которую вы видите. Причина, по которой он так хорошо работает с простыми intтипами, заключается в том, что он распаковывает значение, содержащееся в Integer.

Могу ли я добавить, что если вы делаете то, что делаете, зачем ifначинать это утверждение?

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

4

«==» всегда сравнивает ячейку памяти или ссылки на объекты значений. Метод equals всегда сравнивает значения. Но метод equals также косвенно использует оператор "==" для сравнения значений.

Integer использует кэш Integer для хранения значений от -128 до +127. Если оператор == используется для проверки любых значений в диапазоне от -128 до 127, он возвращает true. для других, чем эти значения он возвращает ложь.

Направить ссылку на какую - то дополнительную информацию


1

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

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

Второй будет автоматически распакован (конечно, вы должны nullсначала проверить s).


0

Помимо этих великих ответов я узнал, что:

НИКОГДА не сравнивайте объекты с ==, если вы не собираетесь сравнивать их по их ссылкам.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.