Что такое load factor в HashMap и как он влияет на производительность?

Ответ

Load factor (коэффициент загрузки) в HashMap — это пороговое значение заполнения, при достижении которого происходит увеличение ёмкости (capacity) и перераспределение элементов (rehashing).

Значение по умолчанию: 0.75f (75%)

Как это работает:

// Создание HashMap с capacity=16 и load factor=0.75 (по умолчанию)
HashMap<String, Integer> map = new HashMap<>();

// При добавлении 12-го элемента (16 * 0.75 = 12)
// происходит resize: capacity увеличивается вдвое до 32
// и все элементы перераспределяются по новым бакетам

Настройка load factor:

// Явное указание capacity и load factor
HashMap<String, Integer> map = new HashMap<>(32, 0.5f);
// Ресайз произойдёт при 16 элементах (32 * 0.5)

Влияние на производительность:

Load factor Преимущества Недостатки
Высокий (0.9-1.0) Меньше ресайзов, экономия памяти Больше коллизий, снижение производительности операций поиска/вставки
Низкий (0.5-0.6) Меньше коллизий, быстрые операции Частые ресайзы, больше потребление памяти
Оптимальный (0.75) Баланс между памятью и производительностью

Практические рекомендации:

  1. Используйте значение по умолчанию (0.75) в большинстве случаев
  2. Если известно точное количество элементов, задавайте начальную capacity:
    // Для 100 элементов с load factor=0.75
    // Начальная capacity = 100 / 0.75 = 134 → округляем до степени двойки: 256
    HashMap<String, Integer> map = new HashMap<>(256);
  3. Низкий load factor полезен для HashMap с коротким временем жизни
  4. Высокий load factor может быть оправдан для HashMap, которые редко изменяются после заполнения

Важно: При resize происходит rehashing — пересчёт хэшей всех элементов и их перераспределение по новым бакетам. Это операция O(n), которая может быть затратной для больших коллекций.

Ответ 18+ 🔞

Да ты посмотри, что за дичь творится в этом вашем HashMap! Ну, типа, представь себе бочку, в которую ты кидаешь свои данные. И есть у этой бочки такой параметр — load factor, или, по-нашему, коэффициент загрузки. Это, блядь, как красная линия на тахометре. Пока не дошёл — всё пиздато, а как перешёл — начинается пиздец под названием resize.

По дефолту эта красная линия стоит на 0.75. То есть, если бочка (capacity) на 16 вёдер, то как только ты нальёшь 12 вёдер воды (16 * 0.75), система охренеет и скажет: «Всё, пиздец, бочка мала!». И начнёт она ресайз — найдёт бочку побольше (в два раза, обычно), и всю воду из старой бочки, сука, по капельке перельёт в новую. Это и есть тот самый rehashing, от которого у всех глаза на лоб лезут при дебаге.

// Вот смотри, создал ты мапу. Бочка на 16, красная линия на 12.
HashMap<String, Integer> map = new HashMap<>();

// Налил 11 вёдер — тишина. Добавил 12-е — ёпта, понеслась!
// Бочка стала на 32, и все ключи, как дурачки, побежали пересчитывать свои хэши.

А если ты, такой умный, заранее знаешь, сколько говна тебе надо хранить? Ну так и скажи об этом! Не заставляй JVM гадать.

// Допустим, у тебя ровно 100 элементов. Считаем: 100 / 0.75 = ~134.
// JVM возьмёт ближайшую степень двойки сверху — 256. Вот и говори сразу!
HashMap<String, Integer> map = new HashMap<>(256);
// Теперь ресайз будет только когда ты реально перевалишь за 192 элемента. Умно, да?

А теперь, сука, самое весёлое — что будет, если поиграться с этой красной линией?

  • Высокий load factor (типа 0.9): Ну, типа, «жаба душит» память тратить. Бочку будем до последнего не менять. Плюс: ресайзов меньше, память экономится. Минус: когда бочка на 90% забита, в ней уже не вода, а каша из коллизий. Искать что-то там — это как в помойке ковыряться. Производительность операций get() и put() летит в пизду.
  • Низкий load factor (типа 0.5): Здесь, блядь, наоборот — «памяти завались, лишь бы быстро было». Бочку будем менять часто, чтоб всегда просторно. Плюс: коллизий почти нет, всё летает. Минус: ресайзы каждые два шага, и половина бочки всегда пустует. Память жрёт, как не в себя.
  • Золотая середина (0.75): А вот это, сука, гениальное решение каких-то умных дядек. Баланс, блядь, идеальный. И память не сильно жрётся, и скорость на уровне. Поэтому его и взяли «из коробки». В 95% случаев лезь со своим loadFactor только если ты реально понимаешь, зачем.

Итог, коротко:

  1. Не трогай load factor, если не уверен на 146%.
  2. Если знаешь точное число элементов — задавай initialCapacity, чтобы ресайз был один раз и сразу.
  3. Помни: rehashing — это дорого. Для огромных мап, которые уже созданы, это может быть заметной операцией. Лучше сразу создать мапу нужного размера, чем потом наблюдать, как она в середине важного процесса вдруг начинает сама с собой переезжать.

Вот и вся магия, ёпта. Не такой уж и страшный зверь, если разобраться. Главное — не лепить его везде бездумно, а то получится как в той поговорке: «Зачем просто, если можно, блядь, сложно?».