Как работает сравнение объектов Integer в Java и особенности кэширования значений
Что проверяют
Знаете ли, что автобоксинг идёт через Integer.valueOf с кэшем, и можете ли объяснить, почему == даёт true для 127 и false для 128. Заодно проверяют понимание разницы между сравнением ссылок и значений.
Как отвечать
Автобоксинг это вызов фабрики. Строка Integer a = 100; компилируется в Integer.valueOf(100). Метод не создаёт объект каждый раз: для значений из кэша он возвращает готовый экземпляр из IntegerCache.
Диапазон кэша по умолчанию от -128 до 127. Нижняя граница фиксирована спецификацией, верхнюю можно поднять флагом JVM -XX:AutoBoxCacheMax=<N>, поэтому формулировка «всегда до 127» неточна.
Integer a = 127, b = 127;
System.out.println(a == b); // true, один объект из кэша
Integer c = 128, d = 128;
System.out.println(c == d); // false, два разных объекта
System.out.println(c.equals(d)); // true== против equals. Для ссылочных типов == сравнивает ссылки, поэтому его результат зависит от того, попало ли значение в кэш. equals сравнивает значения и работает всегда. Для сортировок есть compareTo и Integer.compare.
Смешанное сравнение распаковывает. Если один операнд примитив, обёртка разворачивается, и сравнение идёт по значению:
Integer x = 1000;
int y = 1000;
System.out.println(x == y); // true, x распакован в intОтсюда неприятное следствие: результат зависит от типов операндов, а не только от значений.
new Integer(100) всегда создаёт новый объект в обход кэша, поэтому == с кэшированным даст false. Конструктор помечен устаревшим с Java 9 и предназначен к удалению, единственный правильный способ это Integer.valueOf или автобоксинг.
Кэш есть не только у Integer: Byte, Short и Long кэшируют -128..127, Character значения 0..127, Boolean оба своих значения. У Float и Double кэша нет вовсе, там == между обёртками почти всегда false.
Скрытая ловушка, распаковка null:
Integer v = map.get("missing"); // null
if (v == 1) { ... } // NullPointerException при распаковкеПрактическое правило. Обёртки сравнивать только через equals или Objects.equals, а в горячем коде и в больших коллекциях чисел использовать примитивы или специализированные структуры, чтобы не платить за боксинг и лишние объекты.
Где спотыкаются
Объясняют разницу словами «JVM оптимизирует память» и не могут назвать границу диапазона или воспроизвести её флагом. Ещё чаще забывают про NullPointerException при распаковке null и про то, что сравнение Integer с int идёт по значению.