Menu

Map en Golang : créer, tester une clé, parcourir, trier et supprimer

Les maps Go stockent des paires clé-valeur avec une recherche rapide. Apprenez à les créer, à tester l'existence d'une clé avec comma-ok, à supprimer, à parcourir (dans un ordre aléatoire), à trier les clés, à stocker des structs, et à éviter les panics de map nil et d'écritures concurrentes.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Créer et utiliser une map

Un type map s'écrit map[KeyType]ValueType. Créez-en une avec un littéral ou avec make, puis lisez, écrivez et supprimez par clé.

Sortie :

31
3
map[bob:26 cy:40]
2 0

Deux commodités apparaissent ici. Lire une clé absente renvoie la valeur zéro du type des valeurs (counts['z'] vaut 0), ce qui fait fonctionner le comptage avec m[k]++ sans préparation. Et fmt affiche les maps avec les clés triées, ce qui est pratique pour déboguer mais ne dit rien de l'ordre d'itération.

make(map[K]V, n) accepte une indication de taille facultative. Elle préalloue de la place pour environ n entrées ; contrairement aux slices, une map n'a pas de capacité que l'on peut relire.

Tester l'existence d'une clé : comma-ok

Comme une clé absente se lit comme la valeur zéro, m[k] == 0 ne permet pas de distinguer « absente » de « stockée à 0 ». Utilisez la forme à deux valeurs :

La forme if v, ok := m[k]; ok { ... } limite la portée de v et ok au if. C'est l'une des lignes les plus courantes du code Go.

Supprimer des entrées

delete(m, key) retire l'entrée. Supprimer une clé absente ne fait rien, de même que supprimer dans une map nil. Pour vider toute une map, Go 1.21 a ajouté clear(m), qui garde la map allouée pour qu'elle puisse être réutilisée.

Supprimer des entrées pendant un range sur la même map est permis et sûr. Une entrée supprimée avant que la boucle ne l'atteigne ne sera pas produite.

Parcourir : l'ordre est aléatoire

for k, v := range m visite chaque entrée une fois, dans un ordre non spécifié. Le runtime rend le point de départ aléatoire exprès, donc deux boucles sur la même map dans le même programme ne sont souvent pas d'accord. Lancez ceci plusieurs fois :

Tout code dont la sortie dépend de l'ordre d'une map est un bug qui attend une autre exécution. Les tests qui comparent l'affichage d'une itération de map en sont l'exemple classique.

Clés triées

Pour parcourir une map dans l'ordre des clés, récupérez les clés, triez-les, et indexez la map. Go 1.23 en a fait une seule ligne grâce aux itérateurs des packages maps et slices :

Avec Go 1.22 et avant, maps.Keys n'existait pas dans la bibliothèque standard. L'équivalent est une boucle :

keys := make([]string, 0, len(m))
for k := range m {
	keys = append(keys, k)
}
sort.Strings(keys)

Autres fonctions de maps : maps.Values, maps.Clone (une copie superficielle), maps.Equal, maps.Copy(dst, src) et maps.DeleteFunc.

Types de clés valides

Les clés doivent être comparables avec == : nombres, chaînes, booléens, pointeurs, channels, tableaux de types comparables, structs dont tous les champs sont comparables, et valeurs interface. Les slices, maps et fonctions ne peuvent pas être des clés.

map[[]int]bool{}       // compile error: invalid map key type []int
map[[2]int]bool{}      // fine: arrays are comparable
map[struct{ X, Y int }]string{} // fine: a struct key for a grid position

Une clé struct est la façon idiomatique d'indexer par plusieurs valeurs à la fois, au lieu de concaténer des chaînes.

Les clés interface compilent même quand le type dynamique n'est pas comparable, puis provoquent un panic à l'exécution : stocker un []int dans une map[any]int échoue avec runtime error: hash of unhashable type []int.

Les clés à virgule flottante fonctionnent, mais NaN n'est pas égal à lui-même, donc une clé NaN peut être insérée plusieurs fois sans jamais pouvoir être relue. Évitez les clés flottantes.

Map de structs

Une map peut contenir des structs, mais vous ne pouvez pas affecter un champ d'une struct stockée dans une map, parce que les valeurs d'une map ne sont pas adressables.

Choisissez des valeurs quand les entrées sont petites et remplacées en entier. Choisissez des pointeurs quand vous modifiez souvent des champs ou partagez le même enregistrement depuis plusieurs endroits. Avec des pointeurs, une clé absente renvoie nil, donc ptrs["nope"].Score provoque un panic.

Les maps de slices fonctionnent de la même façon pour les ajouts : groups[k] = append(groups[k], v) ne demande aucune initialisation, parce qu'une clé absente donne une slice nil et que append gère nil.

Les maps se comportent comme des références

Une valeur map fait référence à des données partagées. Affecter une map ou la passer à une fonction ne copie pas les entrées : les deux variables voient la même map.

C'est pourquoi une fonction peut remplir une map sans la renvoyer, contrairement à une slice à laquelle elle ajoute des éléments.

Le panic de la map nil

La valeur zéro d'une map est nil. Une map nil se lit comme une map vide, mais y écrire provoque un panic.

Sortie :

0 0
recovered: assignment to entry in nil map

Le cas de la struct est celui qui piège en pratique. Initialisez les champs map dans un constructeur (func NewCache() *Cache { return &Cache{data: map[string]string{}} }) ou paresseusement avant la première écriture.

Accès concurrent

Les maps ne sont pas sûres en accès concurrent. Si une goroutine écrit pendant qu'une autre lit ou écrit, le runtime peut arrêter le programme avec fatal error: concurrent map writes (ou concurrent map read and map write). C'est une erreur fatale, pas un panic, donc recover ne peut pas l'attraper.

Protégez la map avec un mutex :

Ce code affiche toujours 50 50. Utilisez sync.RWMutex quand les lectures sont bien plus nombreuses que les écritures. sync.Map existe pour deux cas précis (des clés écrites une fois et lues de nombreuses fois, ou des goroutines qui travaillent sur des clés disjointes) ; pour tout le reste, un mutex et une map ordinaire sont plus simples et généralement plus rapides. Plus de détails dans mutex.

Référence rapide

OpérationCode
Créerm := map[string]int{} ou make(map[string]int)
Insérer ou mettre à jourm[k] = v
Lire (zéro si absente)v := m[k]
Tester la présencev, ok := m[k]
Supprimerdelete(m, k)
Tout retirerclear(m) (Go 1.21)
Taillelen(m)
Clés triéesslices.Sorted(maps.Keys(m)) (Go 1.23)
Copiermaps.Clone(m)
Comparermaps.Equal(a, b)

Une map avec des valeurs struct{} est aussi le type ensemble de Go ; voir les ensembles.

Erreurs courantes

  • Écrire dans une map nil. Faites toujours make, y compris pour les champs map des structs.
  • Compter sur l'ordre d'itération. Triez les clés.
  • Utiliser m[k] != 0 comme test de présence. Utilisez comma-ok.
  • Modifier un champ de struct via m[k].Field. Copiez, modifiez et réécrivez, ou stockez des pointeurs.
  • Partager une map entre goroutines sans verrou. Le plantage ne peut pas être récupéré.

Questions fréquentes

Comment vérifier si une clé existe dans une map Go ?

Utilisez la forme à deux valeurs de la recherche : v, ok := m[key]. ok vaut true quand la clé est présente et false sinon, auquel cas v est la valeur zéro. Lire m[key] seul ne permet pas de distinguer une clé absente d'une clé stockée avec la valeur zéro.

Pourquoi l'ordre d'itération des maps Go est-il aléatoire ?

Le langage ne définit pas d'ordre, et le runtime fait délibérément démarrer chaque range à une position aléatoire pour que les programmes ne puissent pas en dépendre. Pour parcourir dans l'ordre des clés, collectez et triez les clés : for _, k := range slices.Sorted(maps.Keys(m)) (Go 1.23).

Comment récupérer toutes les clés d'une map en Go ?

Depuis Go 1.23, maps.Keys(m) renvoie un itérateur ; transformez-le en slice avec slices.Collect(maps.Keys(m)), ou en slice triée avec slices.Sorted(maps.Keys(m)). Avant 1.23, bouclez avec for k := range m et ajoutez chaque clé à une slice.

Pourquoi écrire dans une map provoque-t-il le panic « assignment to entry in nil map » ?

La variable map a été déclarée mais jamais créée : var m map[string]int vaut nil. Lire une map nil renvoie des valeurs zéro, mais y écrire provoque un panic. Créez-la d'abord avec m = make(map[string]int) ou un littéral m := map[string]int{}. Un champ map dans une struct demande la même initialisation.

Les maps Go sont-elles sûres en accès concurrent ?

Non. Des écritures concurrentes, ou une écriture concurrente à des lectures, peuvent faire planter le programme avec fatal error: concurrent map writes, que recover ne peut pas attraper. Protégez la map avec un sync.Mutex ou un sync.RWMutex, ou utilisez sync.Map dans les cas précis pour lesquels elle est conçue.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER