Как происходит расширение (рост) map в Go и что такое рехеширование?

Ответ

Map в Go — это реализация хеш-таблицы, которая состоит из массива "бакетов" (buckets). Рост map происходит, когда среднее количество элементов на один бакет (load factor) превышает определенный порог, который в Go равен 6.5.

Процесс рехеширования

Когда load factor > 6.5, запускается процесс рехеширования:

  1. Выделение новой памяти: Создается новый массив бакетов, обычно в два раза больше предыдущего.
  2. Постепенное копирование (Incremental Rehashing): Вместо того чтобы останавливать все операции и копировать все старые данные сразу, Go делает это постепенно. При каждой последующей операции вставки или удаления элемента, Go переносит несколько элементов из старых бакетов в новые. Это позволяет избежать долгих пауз ("stop-the-world") в работе приложения.
  3. Освобождение старой памяти: После того как все элементы будут перенесены, старый массив бакетов освобождается сборщиком мусора.

Ключевые моменты

  • Порядок итерации: Не гарантируется. Он может меняться даже между запусками программы без изменения самой map.
  • Производительность: В момент рехеширования производительность операций записи может незначительно снижаться из-за дополнительной работы по переносу данных.
  • Оптимизация: Можно заранее задать примерную ёмкость map с помощью make, чтобы избежать многократных рехеширований, если вы знаете примерное количество элементов.
// Предварительное выделение памяти под 100 элементов.
// Это создаст достаточно бакетов, чтобы избежать рехеширования
// до добавления 100-го элемента.
m := make(map[string]int, 100)

m["a"] = 1
m["b"] = 2
// ...