Как работает HashMap

Ответ

HashMap хранит пары ключ-значение в "корзинах" (buckets) на основе хэш-кода ключа. При добавлении элемента:

  1. Вычисляется хэш ключа через hashCode()
  2. По хэшу определяется индекс корзины: index = hash & (capacity - 1)
  3. Если корзина пуста - элемент добавляется
  4. При коллизии (одинаковый хэш) используется цепочка (в Java 8 при длине >8 переходит на красно-черное дерево)

Пример:

HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);  // hash("apple") -> индекс корзины
map.get("apple");     // быстрый доступ по хэшу

Особенности:

  • Не гарантирует порядок
  • Разрешает один null-ключ
  • Потоконебезопасен (используйте ConcurrentHashMap)
  • Начальная емкость 16, load factor 0.75 (автоматическое расширение)

Ответ 18+ 🔞

Э, слушай, вот объясняю на пальцах, как эта штука работает, а то у некоторых от слова "хэш-таблица" уже волнение ебать начинается.

Представь себе этажерку с ящиками, ёпта. Это и есть наш HashMap. Каждый ящик — это "корзина". Когда ты пытаешься что-то туда засунуть, например, пару "яблоко" -> 1, происходит магия, но не сложная.

Сначала HashMap смотрит на ключ ("яблоко") и орет ему: "А ну-ка, дай свой хэш-код, сука!" (hashCode()). Получает какую-то цифру. Потом он берет эту цифру и быстренько вычисляет, в какой именно ящик на этажерке это дело положить. Формула простая: индекс = хэш & (размер_этажерки - 1). Это как взять последние биты номера — и готово, ящик найден.

Дальше три варианта:

  1. Ящик пустой — ура, кладём нашу пару и идём пить чай. Всё быстро.
  2. В ящике уже что-то лежит, но с таким же хэшем (коллизия, блядь!). Тогда HashMap не паникует. Он просто цепляет новую запись в конец цепочки внутри этого ящика (связный список). Как будто вешает на один крючок вторую куртку.
  3. А вот если в одном ящике скопилось овердохуища записей (больше 8, если быть точным), Java говорит: "Ну нахуй этот список, слишком долго искать". И превращает эту кучу внутри ящика в красно-черное дерево. Это как отсортировать вещи в шкафу — искать быстрее.

Вот пример кода, тут всё просто:

HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);  // Вычислили хэш от "apple", нашли ящик, положили.
map.get("apple");     // Снова вычислили хэш, моментально нашли тот же ящик, достали значение. Скорость — пизда рулю.

А теперь держи подводные грабли, на которые все наступают:

  • Порядок? Не, не слышал. Может выдать элементы в каком угодно порядке, хоть завтра, хоть сегодня. Не гарантирует нихуя.
  • null-ключ? Да, пожалуйста, один можно. Положи его на особую полочку.
  • Потоки? Абсолютно потокоНЕбезопасен. Если из двадцати потоков начать в него писать, будет не расширение, а хиросима и нигерсраки. Нужна потокобезопасность — бери ConcurrentHashMap, не выёбывайся.
  • Размер? Создаёшь — по умолчанию даёт 16 ящиков (емкость). И есть "коэффициент заполнения" (load factor) — 0.75. Это значит, как только заполнено 75% ящиков (12 из 16), этажерка говорит: "Всё, я хуйня, меня мало!" — и автоматически увеличивает себя вдвое (до 32), перераспределяя все записи по новым ящикам. Операция дорогая, поэтому если знаешь, что будет много данных, задай начальную емкость сразу.

Короче, штука мощная и быстрая, но знать её нюансы надо, а то подозрение ебать чувствую, что многие тыкают в неё, не глядя.

Видео-ответы