Fondamentaux des dictionnaires en Go

GolangGolangBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Bienvenue, Gophers, dans ce nouveau chapitre.

Dans ce chapitre, nous allons apprendre l'utilisation de base des maps (tableaux associatifs) et analyser la présence de nil en Go.

Une fois ce chapitre terminé, vous pourrez appliquer les maps à vos tâches de développement quotidien.

Points clés de connaissance :

  • Déclaration d'une map
  • Initialisation d'une map
  • Ajout, suppression, mise à jour et recherche d'éléments dans une map
  • Vérification de l'existence d'un élément dans une map
  • Itération sur une map

Introduction aux dictionnaires

Alors, qu'est-ce qu'un dictionnaire?

En informatique, un dictionnaire est une structure de données spéciale composée de paires clé-valeur.

Paire clé-valeur : Une paire d'éléments, où l'un est appelé la clé et l'autre la valeur.

Alors, pourquoi devons-nous utiliser un dictionnaire?

Parce que les dictionnaires sont très efficaces pour les opérations telles que l'ajout, la suppression, la mise à jour et la recherche d'éléments, je suis convaincu que vous trouverez tous cette structure de données très utile.

Description de l'image

Déclaration des maps

Pour une nouvelle structure de données, la première étape consiste à apprendre à la déclarer.

Pour une map, la façon de la définir est la suivante :

var variableName map[keyType]valueType

Par exemple :

var a map[string]int

Cependant, comme une map est un type référence, si nous la déclarons comme dans le code ci-dessus, nous rencontrerons un problème.

Créons un fichier map.go dans le répertoire ~/project :

touch ~/project/map.go

Écrivons le code suivant dans map.go :

package main

func main() {
    // Declare a map named m with key type as string and value type as int
    var m map[string]int
    // Add a data entry "labex"->1 to it
    m["labex"] = 1 // Program crashes
}

Exécutons le programme :

go run map.go

Nous constatons que le programme s'est planté et a lancé une erreur :

panic: assignment to entry in nil map

Cela signifie que l'affectation d'un élément à une nil map est un comportement erroné.

C'est parce que, pour les structures de données telles que les slices, les maps, les canaux et les pointeurs, elles doivent être initialisées avant utilisation.

Et nil est la valeur par défaut initiale d'une map, ce qui signifie qu'aucune mémoire n'est allouée pour la variable lors de la définition.

La valeur initiale nil

Dans la section précédente, nous avons mentionné que c'est un comportement erroné d'affecter un élément à une map nil.

Prenons cette opportunité pour explorer le véritable sens de nil en Go.

L'essence de nil est un identifiant prédéfini.

Pour les types de données de base, leurs valeurs initiales sont différentes :

  • Valeurs booléennes
  • Valeurs numériques
  • Chaînes de caractères

Cependant, pour les slices, les dictionnaires, les pointeurs, les canaux et les fonctions, leur valeur initiale est nil. Ce n'est pas la valeur par défaut initiale avec laquelle nous sommes familiers.

Cela se reflète dans les objets instanciés qui sont affectés à nil. Bien qu'ils puissent être imprimés, ils ne peuvent pas être utilisés.

De plus, il y a quelques points à noter concernant nil.

Non-comparabilité des différents types de nil

package main

import "fmt"

func main() {
    var m map[int]string
    var p *int
    fmt.Printf("%v", m == p)
}

La sortie du programme est la suivante :

invalid operation: m == p (mismatched types map[int]string and *int)

C'est-à-dire que nous ne pouvons pas comparer le nil d'un pointeur de type int avec le nil d'une map.

Ils ne sont pas comparables.

nil n'est pas un mot-clé

package main

import "fmt"

func main() {
    var nilValue = "= =$"
    fmt.Println(nilValue)
}

La sortie du programme est la suivante :

= =$

Nous pouvons définir une variable nommée nil et elle peut être compilée sans erreur. Cependant, nous vous recommandons fortement de ne pas faire cela dans le développement réel.

Non-comparabilité de nil

package main

import "fmt"

func main() {
    fmt.Println(nil == nil)
}

La sortie du programme est la suivante :

invalid operation: nil == nil (operator == not defined on nil)

Déclaration avec le mot-clé make

Après avoir compris le sens de nil, résolvons le problème d'allocation de mémoire pour les dictionnaires.

Ici, nous utilisons le mot-clé make pour allouer de la mémoire.

La fonction make génère une valeur d'un type spécifié qui a été initialisée en tant que valeur de retour.

package main

import "fmt"

func main() {
    var m map[string]int     // Declare a dictionary
    m = make(map[string]int) // Allocate memory to the dictionary
    m["labex"] = 1       // Add data to the dictionary
    fmt.Println(m)
}

La sortie du programme est la suivante :

map[labex:1]

Le code ci-dessus montre comment allouer de la mémoire à un dictionnaire qui a été déclaré en utilisant le mot-clé make.

Nous pouvons également simplifier ce processus :

Écrivez le code suivant dans map.go :

package main

import "fmt"

func main() {
    m := make(map[string]int) // Declare and initialize a dictionary
    m["labex"] = 666      // Add data to the dictionary
    fmt.Println(m)
}

La sortie du programme est la suivante :

map[labex:666]

Le code ci-dessus montre comment déclarer un dictionnaire en utilisant le mot-clé make.

Initialisation manuelle d'une map vide

Dans la section précédente, nous avons montré comment utiliser le mot-clé make pour initialiser une map. Maintenant, explorons une autre méthode : utiliser la syntaxe littérale pour initialiser manuellement une map vide.

Cette méthode est concise et vous permet de créer une map vide prête à l'emploi sans allouer de mémoire explicitement.

Écrivez le code suivant dans map.go :

package main

import "fmt"

func main() {
    // Using literal syntax to initialize an empty map
    m := map[string]int{}

    // Adding elements to the map
    m["labex"] = 777
    m["labs"] = 11

    fmt.Println(m) // Output the map with added elements
}

La syntaxe map[keyType]valueType{} crée une map vide prête à l'emploi. Une fois initialisée, vous pouvez ajouter des éléments à la map en utilisant la syntaxe map[key] = value.

Lorsque vous exécutez le code ci-dessus, le programme affichera :

map[labex:777 labs:11]

Avantages de l'initialisation manuelle :

  • Fournit un moyen rapide de créer une map vide sans utiliser le mot-clé make.
  • Utile lorsque vous avez besoin de commencer avec une map vide et de la remplir dynamiquement.

Initialisation réelle du dictionnaire

Étant donné que nous pouvons initialiser un dictionnaire vide, nous pouvons également lui donner des valeurs initiales.

Écrivez le code suivant dans map.go :

package main

import "fmt"

func main() {
    m := map[string]int{
        "labex": 777,
        "labs": 11,
    }
    m["labby"] = 666
    fmt.Println(m)
}

La sortie du programme est la suivante :

map[labby:666 labs:11 labex:777]

Notez que lors de l'initialisation d'un dictionnaire en Go, une virgule doit être ajoutée après chaque élément, y compris le dernier.

Ajout et mise à jour d'éléments dans un dictionnaire

Ajouter des éléments à un dictionnaire est très simple, comme le montre l'exemple de code ci-dessus.

La syntaxe est la suivante :

dictionaryInstance[keyToAdd] = valueToAdd

Par exemple :

m := map[string]int{}
m["labby"] = 666

Notez que en Go, chaque key dans une map doit être unique.

Écrivez le code suivant dans map.go :

package main

import "fmt"

func main() {
    m := map[string]int{}
    m["labby"] = 666
    m["labby"] = 777
    fmt.Println(m)
}

La sortie du programme est la suivante :

map[labby:777]

Nous avons constaté que la valeur affichée a été modifiée en 777. Autrement dit, si nous écrivons différentes valeurs pour la même key, la value correspondante sera mise à jour avec la nouvelle valeur.

C'est ainsi que nous mettons à jour ou modifions un dictionnaire.

Suppression d'éléments d'un dictionnaire

Comment peut-on supprimer un élément d'un dictionnaire?

Nous devons utiliser la fonction intégrée delete.

Écrivez le code suivant dans map.go :

package main

import "fmt"

func main() {
    m := map[string]int{
        "labex": 777,
        "labs": 11,
        "labby": 666,
    }
    fmt.Println(m)
    delete(m, "labs")
    fmt.Println(m)
}

La sortie du programme est la suivante :

map[labby:666 labs:11 labex:777]
map[labby:666 labex:777]

La fonction delete prend deux arguments. Le premier argument est le dictionnaire sur lequel on veut effectuer l'opération, et le deuxième argument est la clé (key) à supprimer.

Bien sûr, la fonction delete a d'autres utilisations, mais nous ne discuterons que de son utilisation dans les dictionnaires dans ce laboratoire (lab).

Recherche d'éléments dans un dictionnaire

Que se passe-t-il lorsque nous recherchons un élément dans un dictionnaire qui n'existe pas?

package main

import "fmt"

func main() {
    m := map[string]int{
        "labex": 0,
    }
    fmt.Print("The value of labs is: ")
    fmt.Println(m["labs"])
}

La sortie du programme est la suivante :

The value of labs is: 0

Nous avons constaté que si l'élément n'existe pas dans le dictionnaire, interroger ce dernier renverra la valeur par défaut du type de la valeur.

Et pour la clé dans le dictionnaire dont la valeur est 0?

package main

import "fmt"

func main() {
    m := map[string]int{
        "labex": 0,
    }
    fmt.Print("The value of labs is: ")
    fmt.Println(m["labs"])
    fmt.Print("The value of labex is: ")
    fmt.Println(m["labex"])
}

La sortie du programme est la suivante :

The value of labs is: 0
The value of labex is: 0

Nous avons constaté que pour un dictionnaire, la présentation d'une valeur qui n'existe pas et d'une valeur qui existe mais a une valeur par défaut est la même.

Cela nous pose beaucoup de problèmes de compréhension. Comment pouvons-nous résoudre ce problème?

Il s'avère que les développeurs de Go avaient déjà pensé à ce problème. Lorsque nous interrogeons un élément dans un dictionnaire, il y aura une ou deux valeurs de retour de variable.

C'est-à-dire :

labs, ok := m["labs"]

Modifiez map.go :

package main

import "fmt"

func main() {
    m := map[string]int{
        "labex": 0,
    }
    labs, ok := m["labs"]
    fmt.Print("The value of labs is: ")
    fmt.Print(labs)
    fmt.Print(" Does it exist? ")
    fmt.Println(ok)
    labex, ok2 := m["labex"]
    fmt.Print("The value of labex is: ")
    fmt.Print(labex)
    fmt.Print(" Does it exist? ")
    fmt.Println(ok2)
}

La sortie du programme est la suivante :

The value of labs is: 0 Does it exist? false
The value of labex is: 0 Does it exist? true

Maintenant, nous pouvons utiliser la deuxième valeur de retour de la requête pour déterminer si le résultat renvoyé est une valeur par défaut initiale existante ou une valeur par défaut initiale non existante.

Parcours des dictionnaires

Dans certains scénarios, nous devons parcourir un dictionnaire entier, interroger chaque paire clé-valeur (key-value pair) et les traiter. Comment pouvons-nous y parvenir?

package main

import (
    "fmt"
)

func main() {
    m := map[string]int{
        "labex": 777,
        "labs":   11,
        "labby":     666,
    }
    for key, value := range m {
        fmt.Println(key, value)
    }
}

La sortie du programme est la suivante :

labby 666
labs 11
labex 777

À partir de la sortie, nous pouvons voir que la façon de parcourir un dictionnaire est très similaire à celle de parcourir des slices ou des tableaux (arrays).

Résumé

Dans ce laboratoire (lab), nous avons appris l'utilisation fondamentale des dictionnaires, notamment :

  • La déclaration des dictionnaires et comment résoudre les problèmes de déclaration
  • Les caractéristiques de la valeur initiale nil
  • Les méthodes d'initialisation des dictionnaires
  • L'ajout, la suppression, la mise à jour et la recherche d'éléments dans les dictionnaires
  • La détection de l'existence d'éléments dans les dictionnaires
  • Le parcours des dictionnaires