Как Go Map реализует ассоциативную связь?

Ответ

В Go map реализована как хеш-таблица. Ассоциативная связь (сопоставление ключа со значением) достигается благодаря использованию хеш-функции и механизма бакетов:


  1. Хеш-функция: Когда вы добавляете пару ключ-значение в map, Go вычисляет хеш-значение для ключа с помощью встроенной хеш-функции. Это хеш-значение используется для определения, в каком "бакете" (bucket) будет храниться данная пара.



  2. Базовая структура: map состоит из массива бакетов. Каждый бакет — это, по сути, небольшой массив или структура, способная хранить несколько пар ключ-значение.



  3. Разрешение коллизий: Поскольку разные ключи могут давать одинаковое хеш-значение (хеш-коллизия), Go map использует метод цепочек (chaining). Это означает, что внутри каждого бакета хранится список (или массив) пар ключ-значение. Если несколько ключей хешируются в один и тот же бакет, они добавляются в этот список.



  4. Поиск: При поиске значения по ключу, Go снова вычисляет хеш ключа, находит соответствующий бакет, а затем последовательно перебирает элементы внутри этого бакета, сравнивая ключи до тех пор, пока не найдет совпадение.


Пример:

m := make(map[string]int)
m["foo"] = 42 // Хеш от "foo" определяет бакет, где будет храниться 42
m["bar"] = 10 // Хеш от "bar" определяет другой бакет (или тот же, если коллизия)

value := m["foo"] // Вычисляется хеш "foo", находится бакет, затем ищется ключ "foo" внутри бакета
fmt.Println(value) // Выведет 42

Важные особенности Go map:

  • Неупорядоченность: Порядок итерации по элементам map не гарантирован и может меняться от запуска к запуску (это сделано намеренно для предотвращения зависимости от порядка и для защиты от атак на хеш-коллизии).
  • Сравниваемые ключи: Ключи map должны быть сравниваемыми типами (например, числа, строки, указатели, структуры и массивы, содержащие только сравниваемые типы). Слайсы, функции и другие map не могут быть ключами.
  • Недетерминированная хеш-функция: Хеш-функция, используемая Go, может быть недетерминированной (т.е. давать разные хеши для одного и того же ключа при разных запусках программы) для предотвращения атак типа "отказ в обслуживании" (DoS) через хеш-коллизии.