Почему в Java 1 == 1 возвращает true, а 128 == 128 — false

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.


Привет! Давайте поговорим кое о чем, что может удивить вас в Java — почему иногда сравнение чисел с помощью == работает не так, как вы ожидаете. Да, мы говорим о том, почему 1 == 1 возвращает true, а 128 == 128 — false. Звучит странно, правда? Давайте разберемся!

Любопытство: почему 1 == 1 возвращает true, а 128 == 128 — false


Итак, вы могли бы подумать, что при сравнении двух чисел в Java, например 1 == 1 или 128 == 128, результат всегда будет true, потому что, ну, у нас же одно и то же число с обеих сторон равенства, так? Оказывается, в Java не все так просто.

Вот небольшой кусочек кода, который это демонстрирует:
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false
Integer x = 1;
Integer y = 1;
System.out.println(x == y); // true

Так, давайте объясню, что здесь происходит. Это не просто магия. Такие дела происходят из-за кэширования целых чисел в Java. Давайте разберемся детальнее.

Магия кэширования целых чисел


В Java класс Integer определенным образом оптимизирован. Это называется кэшированием целых чисел. Java кэширует объекты Integer для значений в диапазоне от -128 до 127. Зачем? Ну, Java пытается оптимизировать использование памяти, а значения в этом диапазоне используются довольно часто, поэтому вместо создания новых объектов для каждого числа она переиспользует одни и те же.

Вы делаете что-то типа:
Integer x = 1;
Integer y = 1;

А Java не создает два отдельных объекта в памяти для x и y. Вместо этого она переиспользует кэшированный объект Integer для 1. Вот почему уравнение x == y возвращает true. И x, и y обращаются к одному и тому же объекту в памяти.

Но вы можете сделать по-другому:
Integer a = 128;
Integer b = 128;

Поскольку 128 находится за пределами кэшируемого диапазона, Java создает два отдельных объекта Integer. Следовательно, хотя и a, и b имеют значение 128, это два разных объекта в памяти. Вот почему a == b возвращает false — это сравнение двух разных мест в памяти, а не самих значений.



Разрушим представления: == vs .equals()


Теперь мы готовы поговорить о важном различии. В Java оператор == сравнивает ссылки, то есть проверяет, указывают ли две переменные на один и тот же объект в памяти. С другой стороны, метод .equals() сравнивает значения внутри этих объектов.

Давайте чуть изменим код выше:
Integer a = 128;
Integer b = 128;
System.out.println(a.equals(b)); // true

Видите разницу? a.equals(b) возвращает true, потому что сравнивает значение внутри двух объектов Integer, которое равно 128. Его не волнует тот факт, что a и b принадлежат разным объектам.

Диапазон от -128 до 127


Java кэширует значения объекта Integer в диапазоне от -128 до 127. Вы можете воспринимать этот диапазон как золотую середину Java для экономии памяти. Получается, для любого целого числа в этом диапазоне Java повторно использует один и тот же объект. Для значений за пределами этого диапазона, например, 128 или 1000, новые объекты создаются каждый раз.

Вы даже можете изменить этот диапазон, задав аргумент Java Virtual Machine:
-XX:AutoBoxCacheMax=size

Но по умолчанию он зафиксирован на 127.

Пример


Мы используем System.identityHashCode() для отображения хэш-кодов. Эти хэш-коды все еще могут различаться, когда объекты разные (например, c = 128 и d = 128), а могут совпадать, когда обращаются к одному и тому же кэшированному объекту (например, e = 1 и f = 1).
Integer c = 128;
Integer d = 128;
System.out.println(System.identityHashCode(c)); // HashCode of c
System.out.println(System.identityHashCode(d)); // HashCode of d
Integer e = 1;
Integer f = 1;
System.out.println(System.identityHashCode(e)); // HashCode of e (кэшированный объект)
System.out.println(System.identityHashCode(f)); // HashCode of f (тот же кэшированный объект)

Вывод может быть примерно таким:
212628335
2111991224
false
292938459
292938459
true

Для значений от -128 до 127 вы увидите один и тот же хэш-код. Но для значений из-за его пределов (например, 128) Java выделяет разные участки памяти.

Почему это вообще имеет значение


Вы можете получить неожиданные результаты, когда сравниваете числа с помощью == вместо .equals() в вашем коде, особенно в случаях со значениями за пределами кэшируемого диапазона. Так какой вывод мы можем сделать? Используйте .equals() для сравнения значений объектов, если только вам не нужно специально сравнивать участки памяти (что бывает редко в большинстве приложений).

Кэширование целых чисел в Java — это одна из тех удобных оптимизаций, которая отлично работает в большинстве случаев. Но как только вы выходите за пределы диапазона от -128 до 127, все может стать сложнее, если вы полагаетесь на == для сравнения чисел. Просто помните, что для сравнения значений нужно использовать .equals(), и все будет в порядке!
Источник: https://habr.com/ru/companies/selectel/articles/887812/


Интересные статьи

Интересные статьи

Привет. Мы в «СёрчИнформ» проанализировали более 100 открытых вакансий ИБ-специалистов, размещенных на платформах онлайн-рекрутинга. Изучили требуемый опыт, заработную плату, определили регионы и сфер...
Valve выпустила обновление для Counter-Strike 2, в котором разработчики добавили спринт и возможность смены рук с оружием.
Как ускорить 1С БИТРИКС и снизить кол-во запросов к БД используя ядро D7.Пример выборки элементов IBlock с пользовательскими свойствами в один запрос.
Часто при разговорах с клиентами мы спрашиваем, как они ведут учет различных данных и используют ли они CRM-систему? Популярный ответ — мы работаем с Excel-файлами, а пот...
Эта публикация написана после неоднократных обращений как клиентов, так и (к горести моей) партнеров. Темы обращений были разные, но причиной в итоге оказывался один и тот же сценарий, реализу...