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, переопределяется для обеспечения сравнения значений, а не сравнения ссылок на объекты.

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

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

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

mismatch = ( cdiCt != null && cdsCt != null && !cdiCt.equals( cdsCt ) );
Wheaties
источник
4

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

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

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

Виджей
источник
1

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

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

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

Мак Бтон
источник
0

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

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

ZhaoGang
источник