Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Integer Pool в Java: Назначение и работа
Integer Pool (кэш целых чисел) — это оптимизация памяти в Java, которая кэширует объекты Integer для часто используемых значений. Это один из самых интересных примеров того, как JVM оптимизирует использование памяти.
Что такое Integer Pool
Integer Pool — это набор предварительно созданных объектов Integer, которые хранятся в памяти и переиспользуются:
// Integer Pool кэширует значения от -128 до 127
Integer a = 100; // Берётся из кэша
Integer b = 100; // Берётся из кэша (ОДИН И ТОТ ЖЕ ОБЪЕКТ!)
Integer c = 128; // Создаётся НОВЫЙ объект (вне кэша)
System.out.println(a == b); // true (один объект в памяти)
System.out.println(a == c); // false (разные объекты)
System.out.println(a.equals(c)); // true (значения равны)
Почему был придуман Integer Pool
1. Оптимизация памяти
Целые числа от -128 до 127 используются очень часто. Предварительное кэширование экономит миллионы объектов:
// Без кэша — 256 миллионов объектов
for (int i = -128; i <= 127; i++) {
Integer x = i; // Создаёт новый объект каждый раз
}
// С кэшем — только 256 объектов на всё приложение
for (int i = -128; i <= 127; i++) {
Integer x = i; // Переиспользует один из 256 объектов
}
2. Производительность
// Бенчмарк
long start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Integer x = 50; // Из кэша — очень быстро
}
long duration = System.nanoTime() - start;
System.out.println("Cached: " + duration + "ns");
// Vs
start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Integer x = 50000; // Создаёт новый объект каждый раз
}
duration = System.nanoTime() - start;
System.out.println("Uncached: " + duration + "ns");
// Результат: Cached примерно в 2-3 раза быстрее
Как работает Integer Pool
Механизм кэширования
// Автобоксинг переходит через Integer.valueOf()
Integer a = 100; // Эквивалентно Integer.valueOf(100)
// Реализация Integer.valueOf():
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)]; // Из кэша
return new Integer(i); // Новый объект
}
// IntegerCache
private static class IntegerCache {
static final int low = -128;
static final int high = 127;
static final Integer cache[];
static {
// Создаём кэш при загрузке класса
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
}
Правило автобоксинга и valueOf()
Критичная разница
// Автобоксинг
Integer a = 50; // Использует Integer.valueOf(50) — ИЗ КЭША
Integer b = new Integer(50); // Создаёт НОВЫЙ объект (не использует кэш)
System.out.println(a == b); // false (разные объекты)
System.out.println(a.equals(b)); // true (значения равны)
// Правильно сравнивать Integer через equals(), не ==
Полное понимание
public class IntegerPoolDemo {
public static void main(String[] args) {
// 1. Автобоксинг — использует valueOf() — из кэша
Integer a = 10;
Integer b = 10;
System.out.println(a == b); // true (один объект из кэша)
// 2. Явный конструктор — игнорирует кэш
Integer c = new Integer(10);
System.out.println(a == c); // false (разные объекты)
System.out.println(a.equals(c)); // true
// 3. Значения вне кэша
Integer d = 200;
Integer e = 200;
System.out.println(d == e); // false (вне кэша)
System.out.println(d.equals(e)); // true
// 4. valueOf() — использует кэш
Integer f = Integer.valueOf(50);
Integer g = Integer.valueOf(50);
System.out.println(f == g); // true (из кэша)
// 5. Операции над Integer создают новые объекты
Integer h = 100;
Integer i = 50 + 50; // Вычисляется, потом автобоксится
System.out.println(h == i); // true (оба 100, в кэше)
Integer j = 128;
Integer k = 64 + 64; // Вычисляется = 128
System.out.println(j == k); // false (128 вне кэша, разные объекты)
}
}
Похожие кэши для других типов
// Byte: -128 до 127 (полный диапазон типа)
Byte b1 = 10;
Byte b2 = 10;
System.out.println(b1 == b2); // true (есть кэш)
// Short: -128 до 127 (только этот диапазон)
Short s1 = 100;
Short s2 = 100;
System.out.println(s1 == s2); // true
Short s3 = 200;
Short s4 = 200;
System.out.println(s3 == s4); // false (вне кэша)
// Long: -128 до 127
Long l1 = 50L;
Long l2 = 50L;
System.out.println(l1 == l2); // true
// Float, Double: НЕТ кэша
Float f1 = 1.5f;
Float f2 = 1.5f;
System.out.println(f1 == f2); // false (нет кэша)
// Boolean: только 2 объекта (true и false)
Boolean bool1 = true;
Boolean bool2 = true;
System.out.println(bool1 == bool2); // true (один из 2 объектов)
Практические следствия
1. Проблема с == для Integer
public class IntegerComparison {
// Трудноуловимый баг
public static boolean isEqual(Integer a, Integer b) {
return a == b; // НЕПРАВИЛЬНО!
}
public static void main(String[] args) {
System.out.println(isEqual(100, 100)); // true (кэш)
System.out.println(isEqual(1000, 1000)); // false (новые объекты)
// Правильно:
// return a.equals(b) или a.intValue() == b.intValue()
}
}
2. Производительность в циклах
public class PerformanceDemo {
public static void main(String[] args) {
// Быстро — используется кэш
long start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Integer x = 50; // Всегда один объект из кэша
}
System.out.println("Cached: " + (System.nanoTime() - start) + "ns");
// Медленно — создание новых объектов
start = System.nanoTime();
for (int i = 0; i < 10_000_000; i++) {
Integer x = new Integer(50); // Новый объект каждый раз
}
System.out.println("Uncached: " + (System.nanoTime() - start) + "ns");
}
}
3. GC нагрузка
public class GCImpact {
public static void main(String[] args) {
// С кэшем — минимум GC
for (int i = 0; i < 10_000_000; i++) {
Integer x = 50; // Один объект
// Никогда не удаляется из памяти
}
// Без кэша — много GC
for (int i = 0; i < 10_000_000; i++) {
Integer x = new Integer(50); // Новый объект
// Становится мусором в конце итерации
// GC должен очищать миллионы объектов
}
}
}
Когда это важно
1. Collections и HashMap
Map<Integer, String> map = new HashMap<>();
// Кэш помогает здесь (Integer используется в hashCode и equals)
for (int i = -128; i <= 127; i++) {
map.put(i, "value"); // Переиспользуется из кэша
}
// Много объектов создаётся здесь
for (int i = 0; i < 10_000; i++) {
Integer key = i * 1000; // Вне кэша — новый объект
map.put(key, "value");
}
2. Сетевой протокол
public class ProtocolHandler {
public void processPacket(byte[] data) {
int status = data[0]; // Часто 0-127
Integer statusObj = status; // Из кэша
int port = ((data[1] << 8) | data[2]); // Может быть большим
Integer portObj = port; // Может быть вне кэша
}
}
Best Practices
1. Всегда используй equals() для сравнения Integer
// Неправильно
if (a == b) { }
// Правильно
if (a.equals(b)) { }
if (a != null && a.equals(b)) { }
// Или распакуй
if (a.intValue() == b.intValue()) { }
2. Используй примитивный int где возможно
// Не нужно объекты
public void processNumbers(List<Integer> numbers) {
for (Integer num : numbers) {
int value = num; // Распакуй в примитив
doSomething(value);
}
}
// Лучше:
public void processNumbers(int[] numbers) {
for (int num : numbers) { // Примитивные значения
doSomething(num);
}
}
3. valueOf() вместо конструктора
// Неправильно
Integer a = new Integer(50); // Игнорирует кэш
// Правильно
Integer a = Integer.valueOf(50); // Использует кэш
Integer b = 50; // Автобоксинг — использует valueOf()
Выводы
- Integer Pool кэширует значения от -128 до 127
- Используется автоматически при автобоксинге
- Конструктор игнорирует кэш — используй valueOf()
- Всегда сравнивай Integer через equals(), не ==
- Даёт прирост в производительности и памяти
- Важно понимать для интервью и оптимизации
Integer Pool — это отличный пример того, как JVM оптимизирует часто используемые данные. Это простая, но мощная оптимизация, которая экономит память и процессорное время для миллиардов Integer объектов в реальных приложениях.