Внутренняя реализация map в Go
Структура map
Go map — хеш-таблица с открытой адресацией и бакетами по 8 пар ключ-значение.
map[K]V
├── hmap (заголовок)
│ ├── count — число элементов
│ ├── B — log2(количество бакетов)
│ ├── buckets — указатель на массив бакетов
│ └── oldbuckets — указатель при росте (rehash)
└── bmap (бакет, 8 ячеек)
├── tophash[8] — верхние 8 бит хеша каждого ключа
├── keys[8]
└── values[8]
tophash позволяет быстро отсеять несовпадения без полного сравнения ключей.
Особенности поведения
// Нулевое значение при отсутствии ключа
m := map[string]int{"a": 1}
v := m["missing"] // 0, не паника
// Проверка наличия
v, ok := m["missing"]
if !ok { fmt.Println("нет ключа") }
// Удаление
delete(m, "a")
// Итерация — порядок не гарантирован
for k, v := range m { fmt.Println(k, v) }
Порядок итерации
Go намеренно рандомизирует порядок обхода map — защита от кода, случайно зависящего от порядка. Для детерминированного порядка — сортируй ключи:
keys := make([]string, 0, len(m))
for k := range m { keys = append(keys, k) }
sort.Strings(keys)
for _, k := range keys { fmt.Println(k, m[k]) }
Конкурентный доступ
Стандартная map не потокобезопасна. Варианты:
// sync.Mutex вручную
var mu sync.Mutex
mu.Lock()
m[key] = value
mu.Unlock()
// sync.Map — встроенная потокобезопасная реализация
var sm sync.Map
sm.Store("key", 42)
v, ok := sm.Load("key")
sync.Map оптимизирована для случая «много чтений, редкие записи».
Упражнения
| № | Задание | Сложность |
|---|---|---|
| 1 | Упражнение 1 | easy |
| 2 | Упражнение 2 | easy |
| 3 | Упражнение 3 | medium |