Получить доступ

Почему нельзя объявить List<int> и что пишут вместо этого?

JuniorОсновы языка
Ещё спрашивают так:
  • Почему в дженериках нельзя использовать примитивы?
  • Что такое автоупаковка?
  • Чем List<Integer> хуже int[]?
Коротко

Параметр дженерика может быть только ссылочным типом, поэтому пишут List<Integer> с автоупаковкой.

  • Причина — стирание типов: в байткоде параметр становится Object
  • Цена боксинга: объекты в куче, лишняя память и работа GC

А ещё Для больших объёмов чисел — int[], IntStream или специализированные коллекции

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Песочница: == против equals и кэш Integer

Подробно

Дженерики в Java реализованы через стирание типов: после компиляции параметр типа заменяется на его границу, обычно Object. Примитив int не является объектом, поэтому List<int> не компилируется.

Вместо него используют класс-обёртку List<Integer>. Компилятор сам превращает int в Integer при добавлении (автоупаковка, valueOf) и обратно при чтении (распаковка, intValue).

У этого есть цена: каждое число — отдельный объект в куче с заголовком, коллекция хранит ссылки, а не значения. На миллионах элементов это заметно по памяти и скорости, плюс нагрузка на сборщик мусора. Поэтому в горячем коде используют массивы примитивов, IntStream/LongStream и специализированные библиотеки коллекций примитивов.

Ловушки автоупаковки: NullPointerException при распаковке null, сравнение обёрток через ==, а также list.remove(1) у List<Integer> — удаляет элемент по индексу 1, а не значение 1.

Пример

List<Integer> nums = new ArrayList<>();
nums.add(5);                 // автоупаковка: Integer.valueOf(5)
int first = nums.get(0);     // распаковка

nums.remove(Integer.valueOf(5)); // удалить значение 5, а не индекс

int sum = IntStream.rangeClosed(1, 1_000_000).sum(); // без боксинга

Что хотят услышать

  • Дженерики работают только со ссылочными типами — используют обёртку.
  • Связь со стиранием типов.
  • Автоупаковка и её стоимость.
  • Альтернативы для производительности и ловушки — плюс.

Частые ошибки

  • Считать, что обёртка и примитив взаимозаменяемы без накладных расходов.
  • Забывать про скрытый боксинг при вставке в List<Integer>.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Дженерики и коллекции работают только со ссылочными типами, поэтому берут обёртку — List<Integer> (с автоупаковкой при вставке)
  • ✗Можно: List<int> допустим начиная с Java 8
  • ✗int нельзя хранить в коллекциях вообще — только массив int[]
  • ✗List<int> работает, но без автоупаковки

Что спросят дальше

  • Что такое стирание типов и при чём тут это ограничение?
  • Когда вместо List<Integer> взять int[]?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем