
Maps (création, accès, ajout/suppression, vérification d'existence)
Apprenez à créer, manipuler et utiliser les maps en Go : ajout, accès, suppression d'éléments et vérification d'existence de clés avec le pattern 'comma ok'.
Associations clé-valeur : Introduction aux Maps
Après les slices qui gèrent des séquences ordonnées, nous abordons une autre structure de données fondamentale en Go : la map. Une map est une collection non ordonnée de paires clé-valeur. Chaque élément d'une map consiste en une clé unique et une valeur associée. C'est l'équivalent des dictionnaires (Python), tables de hachage (Java), ou tableaux associatifs (PHP).
Les maps sont extrêmement utiles lorsque vous avez besoin d'associer des informations ou d'accéder rapidement à une valeur en utilisant une clé spécifique. Pensez à un annuaire téléphonique (nom -> numéro), aux en-têtes d'une requête HTTP (nom de l'en-tête -> valeur), ou au comptage d'occurrences de mots dans un texte (mot -> nombre d'occurrences).
Go fournit un type `map` intégré, optimisé pour des recherches, ajouts et suppressions rapides (généralement en temps constant en moyenne, O(1)). Ce sous-chapitre couvre les opérations essentielles : comment les créer, y ajouter des données, les lire, vérifier si une clé existe, et supprimer des éléments.
Créer des Maps : Littéraux et `make`
Comme pour les slices, il existe plusieurs façons de créer une map :
1. Avec un littéral de map : C'est une syntaxe concise pour créer et initialiser une map avec quelques paires clé-valeur. La syntaxe est `map[TypeClé]TypeValue{clé1: valeur1, clé2: valeur2, ...}`.
package main
import "fmt"
func main() {
// Map de string (clé) vers int (valeur)
scores := map[string]int{
"Alice": 90,
"Bob": 85,
"Charlie": 95, // La virgule finale est autorisée (et souvent recommandée par go fmt)
}
fmt.Println("Scores:", scores) // L'ordre d'affichage n'est pas garanti !
// Map vide créée avec un littéral
populations := map[string]int{}
fmt.Println("Populations (vide):", populations)
}2. Avec la fonction intégrée `make` : C'est la méthode standard pour créer une map vide (non initialisée avec des valeurs). `make(map[TypeClé]TypeValue)` retourne une map initialisée et prête à l'emploi.
// Crée une map vide de string vers bool
permissions := make(map[string]bool)
permissions["lecture"] = true
permissions["ecriture"] = false
fmt.Println("Permissions:", permissions)
// On peut optionnellement donner une taille initiale indicative (optimisation)
// Cela n'affecte pas le comportement, juste potentiellement les allocations initiales.
comptes := make(map[int]string, 100) // Pré-alloue de l'espace pour environ 100 éléments
fmt.Println("Comptes (vide):", comptes)
3. La map `nil` : La valeur zéro d'un type map est `nil`. Une map `nil` se comporte comme une map vide pour la lecture ou `len()`, mais tenter d'y écrire provoquera une panique (panic). Il faut toujours initialiser une map avec `make` ou un littéral avant d'y ajouter des éléments.
var mapNulle map[string]int
fmt.Println("Map nulle:", mapNulle)
fmt.Println("Longueur map nulle:", len(mapNulle)) // OK, retourne 0
valeur, ok := mapNulle["cle"] // OK pour la lecture (ok sera false)
fmt.Printf("Lecture map nulle: valeur=%d, ok=%t\n", valeur, ok)
// mapNulle["nouvelleCle"] = 10 // ERREUR FATALE : panic: assignment to entry in nil map
// Il faut l'initialiser avant écriture:
mapNulle = make(map[string]int)
mapNulle["cleValide"] = 5 // OK maintenant
fmt.Println("Map initialisée:", mapNulle)
Ajouter et Accéder aux éléments
L'ajout d'une nouvelle paire clé-valeur ou la mise à jour de la valeur d'une clé existante se fait avec la même syntaxe simple d'indexation : `maMap[clé] = valeur`.
couleurs := make(map[string]string)
// Ajout d'éléments
couleurs["rouge"] = "#FF0000"
couleurs["vert"] = "#00FF00"
couleurs["bleu"] = "#0000FF"
fmt.Println("Couleurs après ajout:", couleurs)
// Mise à jour d'un élément existant
couleurs["vert"] = "#008000" // Met à jour la valeur pour la clé "vert"
fmt.Println("Couleurs après MàJ:", couleurs)Pour accéder à la valeur associée à une clé, on utilise également la syntaxe d'indexation : `valeur := maMap[clé]`. Mais attention : si la clé n'existe pas dans la map, cette opération ne provoque pas d'erreur mais retourne la valeur zéro du type des valeurs de la map (par exemple, `0` pour `int`, `""` pour `string`, `false` pour `bool`, `nil` pour les pointeurs/slices/maps).
codeRouge := couleurs["rouge"] // Clé existe
fmt.Println("Code Rouge:", codeRouge) // Affiche: Code Rouge: #FF0000
codeJaune := couleurs["jaune"] // Clé N'EXISTE PAS
fmt.Println("Code Jaune:", codeJaune) // Affiche: Code Jaune:
// (affiche une chaîne vide "", car c'est la valeur zéro pour le type string)
scores := map[string]int{"Alice": 90}
scoreBob := scores["Bob"] // Clé N'EXISTE PAS
fmt.Println("Score Bob:", scoreBob) // Affiche: Score Bob: 0
// (affiche 0, valeur zéro pour le type int)
Ce comportement peut être source d'ambiguïté : le score de Bob est-il vraiment 0, ou la clé "Bob" n'existe-t-elle simplement pas ? C'est là qu'intervient la vérification d'existence.Vérification d'existence : Le pattern "comma ok"
Pour distinguer sans ambiguïté une valeur zéro stockée explicitement d'une valeur zéro résultant d'une clé absente, Go propose une forme spéciale d'accès à la map qui retourne deux valeurs : la valeur (ou la valeur zéro si absente) et un booléen indiquant si la clé existait réellement.
Syntaxe : `valeur, ok := maMap[clé]`
- `valeur` : contient la valeur associée à la `clé` si elle existe, sinon la valeur zéro du type.
- `ok` : est un booléen, `true` si la `clé` était présente dans la map, `false` sinon.
C'est le moyen idiomatique et sûr de vérifier l'existence d'une clé avant d'utiliser sa valeur :
scores := map[string]int{"Alice": 90, "David": 0}
// Vérifier pour Alice
scoreAlice, okAlice := scores["Alice"]
if okAlice {
fmt.Printf("Alice existe, son score est %d\n", scoreAlice)
} else {
fmt.Println("Alice n'existe pas.")
}
// Sortie: Alice existe, son score est 90
// Vérifier pour David (existe avec valeur 0)
scoreDavid, okDavid := scores["David"]
if okDavid {
fmt.Printf("David existe, son score est %d\n", scoreDavid)
} else {
fmt.Println("David n'existe pas.")
}
// Sortie: David existe, son score est 0
// Vérifier pour Bob (n'existe pas)
scoreBob, okBob := scores["Bob"]
if okBob {
fmt.Printf("Bob existe, son score est %d\n", scoreBob)
} else {
// okBob est false, scoreBob est la valeur zéro (0)
fmt.Println("Bob n'existe pas.")
}
// Sortie: Bob n'existe pas.
// On peut aussi combiner dans un if court (très courant) :
if scoreEve, ok := scores["Eve"]; ok {
fmt.Printf("Eve existe, score : %d\n", scoreEve)
} else {
fmt.Println("Eve n'existe pas")
}
// Sortie: Eve n'existe pasCe pattern `val, ok := map[key]` est fondamental lorsque vous travaillez avec des maps en Go.
Supprimer des éléments : La fonction `delete`
Pour supprimer une paire clé-valeur d'une map, Go fournit la fonction intégrée `delete`. Elle prend en arguments la map et la clé à supprimer.
Syntaxe : `delete(maMap, clé)`
Si la clé spécifiée existe dans la map, l'entrée correspondante est supprimée. Si la clé n'existe pas, `delete` ne fait rien et ne provoque pas d'erreur (c'est une opération "no-op").
permissions := map[string]bool{
"lecture": true,
"ecriture": true,
"execution": false,
}
fmt.Println("Avant delete:", permissions)
delete(permissions, "ecriture") // Supprime la clé "ecriture"
fmt.Println("Après delete 'ecriture':", permissions)
delete(permissions, "admin") // Clé "admin" n'existe pas, ne fait rien
fmt.Println("Après delete 'admin':", permissions)
Sortie :Avant delete: map[ecriture:true execution:false lecture:true] // Ordre non garanti
Après delete 'ecriture': map[execution:false lecture:true]
Après delete 'admin': map[execution:false lecture:true]Types de clés et Itération
Un point important concernant les maps en Go est que le type de la clé doit être comparable. Cela signifie que les valeurs de ce type doivent pouvoir être comparées en utilisant les opérateurs `==` et `!=`. Les types de base (entiers, flottants, booléens, chaînes), les pointeurs, les canaux (channels) et les types structurés dont tous les champs sont comparables peuvent être utilisés comme clés de map. En revanche, les slices, les maps elles-mêmes et les fonctions ne peuvent pas être utilisées comme clés car elles ne sont pas directement comparables.
Enfin, comme mentionné précédemment, vous pouvez itérer sur les paires clé-valeur d'une map en utilisant la boucle `for range` : `for key, value := range maMap { ... }`. Rappelez-vous que l'ordre d'itération n'est pas défini et peut varier.
Les maps sont une structure de données extrêmement polyvalente et performante en Go, indispensable pour de nombreuses tâches de programmation allant de la simple configuration au traitement de données complexes.