У Go map — це вбудований тип даних, що представляє хеш-таблицю.
В інших мовах програмування те саме може називатися dict, асоціативним масивом або сховищем ключ-значення.
Якщо така концепція нам незнайома, map можна уявити як словник, де кожне слово — це key, а визначення — element цього map.
Перш ніж почнемо, радимо зазирнути до специфікації Go для
mapта блогу Go проmap, щоб глибше розібратися зmap.
Синтаксично map має такий вигляд:
map[KeyType]ElementType
KeyType має бути будь-яким порівнюваним типом, а ElementType може бути будь-яким припустимим типом у Go, тобто можна зберігати будь-що, від примітивної змінної до слайса.
Важливо памʼятати, що map у Go невпорядкований: якщо спробувати перебрати map циклом і надрукувати елементи, можна здивуватися, побачивши, що вони виводяться у випадковому порядку (спробуйте, якщо хочеться).
Також важливо знати, що кожен ключ унікальний: присвоєння того самого ключа вдруге перезапише значення, що відповідає цьому ключу.
map — це посилальний тип: якщо передати його далі, Go не копіюватиме map повністю.
Натомість Go скопіює вказівник на map, і саме тому передавати map у функцію чи змінну дешево.
Значення неініціалізованого map — це nil.
Визначити map можна так (це ще називають nil map);
var foo map[string]int
Щоб ініціалізувати map, можна зробити так:
// With map literal
foo := map[string]int{}
або
// or with make function
foo := make(map[string]int)
nilmap відрізняється від ініціалізованогоmap: запис уnilmap спричинить помилку під час виконанняpanic: assignment to entry in nil mapТому важливо ініціалізувати
mapперед використанням
Ось деякі операції, які можна виконувати з map:
// Add a value in a map with the `=` operator:
foo["bar"] = 42
// Here we update the element of `bar`
foo["bar"] = 73
// To retrieve a map value, you can use
baz := foo["bar"]
// To delete an item from a map, you can use
delete(foo, "bar")
Якщо спробувати отримати значення для ключа, якого немає в map, повернеться нульове значення відповідного типу.
Це може заплутати, особливо якщо типове значення ElementType (наприклад, 0 для int) є припустимим значенням.
Щоб перевірити, чи існує ключ у map, можна скористатися таким:
value, exists := foo["baz"]
// If the key "baz" does not exist,
// value: 0; exists: false
Як ми вже бачили, визначити, чи ініціалізовано map, легко: достатньо порівняти його з nil. Але що з іншими map? Оскільки map не є порівнюваним типом, оператор == тут не підходить, але на допомогу приходить пакет reflect із функцією DeepEqual, яку можна використати так:
import "reflect"
// ....
equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true
Але тут виникає питання: якщо map не порівнюваний, чому його можна порівнювати з nil? Специфікація зробила для цього виняток, див. розділ про порівнюваність
І останнє: якщо писати в map з кількох горутин, це активує детектор гонок, див. документацію Go та блог Go.
Або можна використати sync.Map, atomic чи mutex, щоб обійти цю проблему.