Pas de Set en Go : pourquoi et comment vivre sans ensemble intégré

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Où est mon Set ? » — le cri du cœur du développeur Go

\n\n

Si vous venez à Go depuis Python, JavaScript ou Java, vous vous demanderez tôt ou tard : « Mais où diable est mon cher Set ? » — et c'est normal. Sur Stack Overflow, le sujet « Golang why don't we have a set datastructure » a rassemblé des milliers de vues et des dizaines de discussions animées. Les développeurs sont habitués à un ensemble pratique avec des éléments uniques, alors que Go ne propose que des tableaux, des slices et des maps. Dans cet article, nous analyserons la philosophie des créateurs de Go, apprendrons à émuler un Set et même à écrire notre propre implémentation minimaliste.

\n\n

Pourquoi les créateurs de Go n'ont-ils pas ajouté Set à la bibliothèque standard ?

\n\n

La réponse réside dans le minimalisme et le pragmatisme du langage. L'équipe Go (Rob Pike, Ken Thompson, Robert Griesemer) a adhéré au principe : « Si quelque chose peut être fait avec une seule construction du langage, ne multiplie pas les entités ». En Go, map n'est pas seulement un dictionnaire, mais un conteneur universel. Il prend déjà en charge l'unicité des clés, ce qui est la propriété principale d'un ensemble.

\n\n

Philosophie « moins de magie — plus de clarté »

\n\n

Contrairement à Python, où set est un type séparé avec ses propres méthodes (.add(), .union()), en Go, ils ont décidé de ne pas complexifier le langage. Pourquoi écrire une structure séparée si map[T]struct{} fait la même chose ? Cela économise de l'espace dans la bibliothèque standard et abaisse le seuil d'entrée pour les débutants. L'essentiel est d'apprendre à utiliser correctement map comme un set.

\n\n

Comment émuler un Set en Go : trois approches fonctionnelles

\n\n

Passons à la pratique. Voici comment implémenter un ensemble en utilisant uniquement les outils standards de Go.

\n\n

1. La plus populaire : map[type]struct{}

\n\n

C'est l'approche standard dans la communauté Go. Pourquoi struct{} ? Parce que la structure vide n'occupe pas de mémoire (0 octet) — idéale pour marquer l'« existence » d'un élément.

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    // Création d'un ensemble d'entiers\n    set := make(map[int]struct{})\n\n    // Ajout d'éléments\n    set[1] = struct{}{}\n    set[2] = struct{}{}\n    set[3] = struct{}{}\n    set[1] = struct{}{} // Le doublon est ignoré\n\n    // Vérification d'existence\n    if _, exists := set[2]; exists {\n        fmt.Println("2 est dans l'ensemble")\n    }\n\n    // Suppression\n    delete(set, 3)\n\n    // Parcours\n    for key := range set {\n        fmt.Println(key)\n    }\n}
\n\n

Avantages : aucune allocation mémoire pour les valeurs, simplicité. Inconvénients : pas de méthodes .Add() ou .Contains() — tout se fait via la syntaxe de map.

\n\n

2. Type personnalisé avec méthodes

\n\n

Si vous avez besoin d'une API propre, encapsulez map dans une structure :

\n\n
package main\n\nimport "fmt"\n\n// Set — ensemble générique (Go 1.18+)\ntype Set[T comparable] struct {\n    items map[T]struct{}\n}\n\n// NewSet crée un nouveau Set\nfunc NewSet[T comparable]() *Set[T] {\n    return &Set[T]{items: make(map[T]struct{})}\n}\n\n// Add ajoute un élément\nfunc (s *Set[T]) Add(item T) {\n    s.items[item] = struct{}{}\n}\n\n// Contains vérifie la présence\nfunc (s *Set[T]) Contains(item T) bool {\n    _, ok := s.items[item]\n    return ok\n}\n\n// Size retourne la taille\nfunc (s *Set[T]) Size() int {\n    return len(s.items)\n}\n\nfunc main() {\n    intSet := NewSet[int]()\n    intSet.Add(10)\n    intSet.Add(20)\n    fmt.Println(intSet.Contains(10)) // true\n    fmt.Println(intSet.Size())       // 2\n}
\n\n

Cela ressemble déjà à une structure de données set complète en Go. Les generics sont apparus dans la version 1.18, donc ce code fonctionne sur les projets modernes.

\n\n

3. Utilisation de bool au lieu de struct{}

\n\n

Certains utilisent map[T]bool, mais cela gaspille 1 octet par valeur. Dans la plupart des cas, la différence est négligeable, mais si vous écrivez un service à forte charge, il est préférable de s'en tenir à struct{}.

\n\

Blogs

Book Recommendations