Go intègre un type natif qui implémente des tables de hachage appelées map. Il s'agit d'un type de données composé d'une collection de clés uniques et d'une collection de valeurs pour chacune de ces clés.
Il peut être comparé à un dictionnaire dans d’autres langues, par exemple, qui stocke des paires clé-valeur. Ces valeurs sont accessibles à l'aide de clés, de la même manière que les tableaux et les tranches comme nous l'avons vu dans le post précédent.
Les index ne sont pas limités à un nombre comme dans les tableaux ou les tranches et les éléments ne sont pas ordonnés, donc si nous imprimons une carte elle renverra un ordre aléatoire, si nous ne faisons rien pour remplacer son impression et forcer l'ordre souhaité.
Pour déclarer une carte, cela se fait avec map[key]value, où key sera le type que nous voulons que notre clé soit (elle doit être d'un type comparable https://go.dev/ref/spec#Comparison_operators ) et value sera le type avec lequel nous voulons que la carte soit stockée dans chacune des clés, quel que soit son type, d'un int à une structure, ou une autre carte, comme nous le voulons.
Comme pour les tranches, les cartes sont des types référencés, ce qui signifie que la valeur nulle d'une carte sera nulle.
Cela se produit parce qu'en dessous se trouve une table de hachage qui stocke les clés et les valeurs, et elles n'en sont qu'une enveloppe, une abstraction.
Si nous le déclarons comme :
var m map[int]int
sa valeur sera nulle.
Si nous voulons qu'il ait une valeur nulle, nous pouvons utiliser la déclaration :
m := map[int]int{}
Et on peut même l'initialiser tout comme les slices, en utilisant la fonction make.
m := make(map[string]string)
Faire cela initialisera une carte de hachage avec le pool de mémoire approprié, renvoyant ainsi une carte qui pointe vers cette structure de données.
L'ajout de valeurs à une carte se fait en utilisant des accolades [] et l'accolade, tout comme avec des tableaux ou des tranches. Dans cet exemple, nous allons créer une carte avec les clés étant des chaînes et les valeurs étant des entiers, pour stocker les noms et les âges.
ages := make(map[string]int) ages["John"] = 33 ages["Charly"] = 27 ages["Jenny"] = 45 ages["Lisa"] = 19
Si nous voulons y ajouter les valeurs lorsque nous déclarons la carte, nous pouvons utiliser la déclaration courte et tout faire en la même étape :
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
Pour lire les valeurs, il suffit d'indiquer la clé de notre carte et elle renverra cette valeur. Par exemple, pour connaître l'âge de Lisa, on peut faire :
fmt.Println(ages["Lisa"]) // 19
Si nous essayons d'accéder à une clé qui n'existe pas, la valeur obtenue sera la valeur zéro du type, dans ce cas ce serait "", puisqu'il s'agit d'une chaîne.
Afin de vérifier si un élément existe dans la carte, nous pouvons vérifier si le type est celui par défaut, mais ce n'est pas très fiable, car peut-être qu'il existe mais sa valeur est une chaîne vide ou 0 dans le cas de int , qui correspondrait à sa valeur zéro, donc Go nous aide avec ce qui suit :
val, ok := ages["Randy"]
Si nous égalisons la carte à deux valeurs, la première sera la valeur de cet élément accessible via la clé, dans ce cas "Randy" qui n'existe pas, et la seconde sera un booléen, qui indiquera s'il existe ou non.
Si la valeur ne nous intéresse pas et que nous voulons simplement vérifier l'existence d'une clé, nous pouvons utiliser _ pour ignorer la valeur comme suit :
_, ok := ages["Randy"]
Comme pour les tableaux et les tranches, nous pouvons utiliser la fonction len pour savoir combien d'éléments il y a dans la carte.
fmt.Println(len(ages)) // 4
Si nous voulons modifier une valeur, c'est aussi simple que d'accéder à ladite valeur à l'aide d'une clé et de la faire correspondre avec une autre, et elle sera modifiée.
Si nous déclarons une deuxième carte pointant vers la première, si nous modifions la valeur de la seconde, puisqu'il s'agit d'un type référencé, nous modifierons la valeur de la première, car les deux partagent la même table de hachage en dessous.
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19} agesNew := ages agesNew["Bryan"] = 77 fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19] fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
Pour supprimer des éléments d'une carte, Go nous met à disposition une fonction de suppression avec la signature suivante delete(m map[Type]Type1, key Type) qui reçoit une carte et la clé à supprimer.
Dans le cas précédent, si on voulait éliminer "Lisa" on le ferait :
delete(ages, "Lisa")
Si on veut parcourir le contenu d'une map, on peut le faire en utilisant un for avec la variation de range que l'on a déjà vu dans le post sur les tableaux et les tranches.
Comme alors, le premier élément sera l'index, donc la clé, et le second la valeur.
for key, value := range ages { fmt.Printf("%s: %d\n", key, value) } // Output: // Jenny: 45 // Lisa: 19 // John: 33 // Charly: 27
Comme pour les tableaux et les tranches, si on s'intéresse uniquement à la valeur, sans la clé, on peut l'omettre en utilisant _.
for _, value := range ages { fmt.Println(value) } // Output: // 19 // 33 // 27 // 45
Et si ce qui nous intéresse est simplement la clé, on peut attribuer la plage à une seule variable pour l'obtenir :
for key := range ages { fmt.Println(key) } // Output: // John // Charly // Jenny // Lisa
As I mentioned in the introduction, in a map the information is not ordered, so when looping through it we cannot specify what order it follows, nor can Go guarantee that the order between executions is the same.
As we saw with arrays and slices, in the standard library there is a sort package which helps us sort elements: https://pkg.go.dev/sort
Following our example with ages and using sort, we can sort the keys of the map before traversing it and thus guarantee that it will be accessed in order.
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19} keys := make([]string, 0, len(ages)) for k := range ages { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println(k, ages[k]) } // Output: // Charly 27 // Jenny 45 // John 33 // Lisa 19
We declare our ages map with the short declaration as we saw before.
We create a string slices to store the keys and use the make method with 0 length, since we do not have any keys at the moment, but we do reserve the capacity it will have using the len method for the length of our map.
We go through the ages map to keep its keys and add them to the created slice.
We sort the keys alphabetically with the sort.Strings function.
We go through the slice of keys, already ordered, and access the map with the key in question.
This way we will access the map in an orderly manner and we can do the logic that our program needs.
Something to keep in mind with maps is that they are not safe to use concurrently. If these are concurrent reads, either accessing a value or through a for with a range, there is no problem with multiple goroutines accessing it at the same time.
The problematic case is when you want to update the value of a map concurrently, either by adding or removing elements from it, and at the same time you are reading it from another side, for example.
To solve this situation there are several possible solutions, which I will not go into much detail, I will simply mention and leave it to your choice to delve deeper into them.
If we use the sync package: https://pkg.go.dev/sync from the standard library, we can control the synchrony between the different goroutines.
A possible use is the RWMutex type which allows us to lock and unlock reads and writes to a type. So if we have a type that contains a sync.RWMutex and a map we can control when it can be accessed.
Another interesting type to investigate within the same sync package is Map, which already offers us a series of functions that will help us work with our map, which in the end we will not be able to work with natively, as with the previous solution.
Depending on the use case we are implementing, one or the other will be more useful to us, and there is no one better than the other, it will always depend on what we need.
I hope everything that I have tried to explain in this post has been clear, and please if there is any part that has not been completely clear or there are parts that I have not covered that you would like me to do, leave me a comment right here or through my social networks that you have on my profile and I will be happy to respond.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!