En Go, map est un type de données intégré qui représente une table de hachage. Dans d'autres langages de programmation, tu connais peut-être map sous le nom de dict, de tableau associatif ou de stockage clé/valeur. Si ce concept ne t'est pas familier, tu peux voir map comme un dictionnaire, où chaque mot est la key et sa définition est l'element de la map.
Avant de commencer, je t'invite à consulter la spécification Go sur les maps et le blog Go sur les maps pour approfondir
map.
Syntaxiquement, map ressemble à ceci :
map[KeyType]ElementType
KeyType doit être n'importe quel type comparable, tandis que ElementType peut être n'importe quel type valide en Go, ce qui signifie que tu peux y stocker aussi bien une variable primitive qu'un slice.
Il est important de se rappeler qu'en Go, les map ne sont pas ordonnées : si tu essaies de parcourir une map et d'afficher ses éléments, tu risques d'être surpris de les voir apparaître dans un ordre aléatoire (essaie, si tu veux).
Il est également important de savoir que chaque clé est unique, ce qui signifie qu'affecter deux fois la même clé écrase la valeur associée.
map est un type de référence, ce qui signifie que si tu la transmets, Go ne copie pas toute la map. À la place, Go copie le pointeur de la map, ce qui rend peu coûteux le passage d'une map à une fonction ou à une variable. La valeur d'une map non initialisée est nil.
Tu peux définir une map comme ceci (on appelle aussi cela une map nil) ;
var foo map[string]int
Pour initialiser une map, tu peux faire :
// With map literal
foo := map[string]int{}
ou
// or with make function
foo := make(map[string]int)
Une map nil est différente d'une map initialisée : écrire dans une map nil provoque une erreur d'exécution
panic: assignment to entry in nil mapIl est donc important d'initialiser une map avant de l'utiliser
Voici quelques opérations que tu peux effectuer sur une 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")
Si tu essaies de récupérer la valeur d'une clé qui n'existe pas dans la map, tu obtiens la valeur zéro du type de valeur. Cela peut prêter à confusion, surtout si la valeur par défaut de ton ElementType (par exemple 0 pour un int) est une valeur valide. Pour vérifier si une clé existe dans ta map, tu peux utiliser
value, exists := foo["baz"]
// If the key "baz" does not exist,
// value: 0; exists: false
Comme on l'a vu plus haut, détecter si une map est initialisée ou non est facile : il suffit de la comparer à nil. Mais qu'en est-il des autres maps ? Eh bien, comme map n'est pas comparable, on ne peut pas utiliser l'opérateur ==, mais le paquet reflect a quelque chose pour nous aider : DeepEqual. On peut l'utiliser comme ceci
import "reflect"
// ....
equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true
Mais attends, si map n'est pas comparable, pourquoi peut-on la comparer avec nil ? Eh bien, la spécification a fait une exception pour cela, voir la spec sur les types comparables
Enfin, si tu essaies d'écrire dans une map depuis plusieurs goroutines, cela déclenchera le détecteur de courses, voir la documentation Go et le blog Go. Sinon, tu peux utiliser sync.Map, atomic ou mutex pour contourner ce problème.