Menu

Set en Golang : map[T]struct{}, opérations et set générique

Go n'a pas de type ensemble intégré. L'idiome standard est une map avec des valeurs struct vides. Découvrez l'ajout, le test d'appartenance et la suppression, l'union, l'intersection et la différence, et comment écrire un petit Set générique.

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

L'idiome : une map aux valeurs vides

Go n'a pas de mot-clé set ni d'ensemble dans la bibliothèque standard. Une map dont les clés sont les éléments et dont les valeurs ne portent rien fait l'affaire.

struct{} est le type struct vide et struct{}{} sa seule valeur. Elle occupe zéro octet, donc la map ne stocke que les clés.

Toutes les règles des maps s'appliquent : les clés doivent être comparables, l'ordre d'itération est aléatoire, une map nil provoque un panic à l'écriture, et les écritures concurrentes demandent un verrou. La page sur les maps traite chacune d'elles.

map[T]struct{} ou map[T]bool

L'autre écriture courante est map[T]bool. Elle se lit mieux parce qu'une clé absente renvoie false :

seen := map[string]bool{}
seen["a"] = true
if seen["a"] { ... }

Les compromis :

map[T]struct{}map[T]bool
Taille de la valeur0 octet1 octet (plus l'alignement)
Test d'appartenance_, ok := s[k]s[k]
Ambiguïtéaucunes[k] = false est un troisième état

La troisième ligne est la vraie raison pour laquelle beaucoup de bases de code préfèrent la struct vide : avec bool, quelqu'un finit par écrire s[k] = false et len(s) cesse d'être le nombre de membres. Pour de petits ensembles, la différence de mémoire n'a pas d'importance.

Supprimer les doublons d'une slice

L'usage le plus courant d'un ensemble est la déduplication. Ce code garde la première occurrence de chaque valeur et préserve l'ordre :

Sortie :

[b a c]
[3 1 2]
[1 2 3]

Union, intersection, différence

L'algèbre des ensembles tient en quelques boucles. Parcourez le plus petit ensemble quand vous testez l'appartenance à l'autre, puisque chaque recherche prend en moyenne un temps constant.

sorted n'existe que pour rendre la sortie stable. Afficher un ensemble en le parcourant donne un ordre différent à chaque exécution. maps.Keys et slices.Sorted demandent Go 1.23.

Un type nommé comme type set map[string]struct{} reste une map : vous l'indexez, faites un range dessus et un delete de la même façon, et vous pouvez lui ajouter des méthodes.

Un petit Set générique

Avec les génériques (Go 1.18), un seul type couvre tous les types d'éléments comparables. C'est suffisant pour la plupart des programmes :

Envelopper la map dans une struct masque le bruit de struct{}{} et garantit que la map est créée par le constructeur, ce qui supprime le panic de la map nil. Sorted ne peut pas être une méthode : une méthode ne peut ni déclarer ses propres paramètres de type ni resserrer la contrainte comparable du type, et le tri demande cmp.Ordered. La page sur les génériques explique les contraintes.

Si vous avez besoin d'un ensemble complet (variantes sûres en concurrence, nombreuses opérations), des packages tiers comme github.com/deckarep/golang-set existent. Pour la plupart du code, l'idiome de la map ou un type de 30 lignes comme celui-ci est ce qu'utilisent les développeurs Go.

Ensembles de structs

Tout type comparable peut être un élément, y compris les structs aux champs comparables. Les tests du genre « ai-je déjà vu cette paire » deviennent directs :

type edge struct{ from, to string }
visited := map[edge]struct{}{}
visited[edge{"a", "b"}] = struct{}{}

Les slices et les maps ne peuvent pas être des éléments d'ensemble. Pour suivre des slices uniques, convertissez d'abord chacune en clé comparable, par exemple un tableau de taille fixe, ou une chaîne construite avec fmt.Sprint.

Erreurs courantes

  • Oublier d'initialiser. var s map[string]struct{} vaut nil ; le premier ajout provoque un panic.
  • Afficher un ensemble et s'attendre à une sortie stable. Triez d'abord les membres.
  • Utiliser map[T]bool et stocker false. len ne compte alors plus les membres. Utilisez delete pour retirer.

Questions fréquentes

Go a-t-il un type ensemble ?

Non. La bibliothèque standard n'a pas de set. L'idiome est une map dont les valeurs ne portent aucune information : map[string]struct{}. Ajouter s'écrit s[k] = struct{}{}, tester l'appartenance _, ok := s[k], supprimer delete(s, k), et la taille vaut len(s).

Faut-il utiliser map[T]bool ou map[T]struct{} pour un ensemble en Go ?

map[T]struct{} rend l'intention explicite et ses valeurs occupent zéro octet. map[T]bool se lit plus naturellement (if seen[x]) parce qu'une clé absente renvoie false. Les deux sont corrects ; la différence de mémoire ne compte que pour de très grands ensembles. Choisissez-en un et restez cohérent.

Comment supprimer les doublons d'une slice en Go ?

Pour garder la première occurrence dans l'ordre, bouclez et notez les valeurs déjà vues dans une map[T]struct{}, en n'ajoutant que celles qui n'ont pas été vues. Si l'ordre n'a pas d'importance, triez et retirez les doublons adjacents : slices.Sort(s); s = slices.Compact(s).

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER