Go hat kein Set: Warum und wie man ohne eingebaute Menge lebt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

„Wo ist mein Set?“ – ein Schrei aus der Seele eines Go-Entwicklers

\n\n

Wenn Sie von Python, JavaScript oder Java zu Go kommen, werden Sie sich früher oder später fragen: „Wo zum Teufel ist mein geliebtes Set?“ – und das ist völlig normal. Auf Stack Overflow hat das Thema „Golang why don't we have a set datastructure“ tausende Aufrufe und Dutzende hitzige Diskussionen gesammelt. Entwickler sind an die praktische Menge mit eindeutigen Elementen gewöhnt, aber Go bietet nur Arrays, Slices und Maps. In diesem Artikel analysieren wir die Philosophie der Go-Entwickler, lernen, wie man ein Set emuliert, und schreiben sogar unsere eigene minimalistische Implementierung.

\n\n

Warum haben die Entwickler von Go kein Set in die Standardbibliothek aufgenommen?

\n\n

Die Antwort liegt im Minimalismus und Pragmatismus der Sprache. Das Go-Team (Rob Pike, Ken Thompson, Robert Griesemer) folgte dem Prinzip: „Wenn etwas mit einer einzigen Sprachkonstruktion erreicht werden kann, schaffe keine zusätzlichen Entitäten.“ In Go ist map nicht nur ein Wörterbuch, sondern ein universeller Container. Es unterstützt bereits die Eindeutigkeit von Schlüsseln, was die Haupteigenschaft einer Menge ist.

\n\n

Philosophie „weniger Magie – mehr Klarheit“

\n\n

Im Gegensatz zu Python, wo set ein eigener Typ mit eigenen Methoden (.add(), .union()) ist, hat man sich in Go entschieden, die Sprache nicht zu verkomplizieren. Warum eine separate Struktur schreiben, wenn map[T]struct{} dasselbe tut? Das spart Platz in der Standardbibliothek und senkt die Einstiegshürde für Anfänger. Die Hauptsache ist, zu lernen, map richtig als Set zu verwenden.

\n\n

Wie man ein Set in Go emuliert: drei funktionierende Ansätze

\n\n

Kommen wir zur Praxis. Hier ist, wie man eine Menge nur mit den Standardmitteln von Go implementieren kann.

\n\n

1. Der beliebteste: map[type]struct{}

\n\n

Dies ist der Standardansatz in der Go-Community. Warum struct{}? Weil die leere Struktur keinen Speicher belegt (0 Byte) – ideal als Markierung für die „Existenz“ eines Elements.

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    // Erstellen einer Menge von ganzen Zahlen\n    set := make(map[int]struct{})\n\n    // Elemente hinzufügen\n    set[1] = struct{}{}\n    set[2] = struct{}{}\n    set[3] = struct{}{}\n    set[1] = struct{}{} // Duplikat wird ignoriert\n\n    // Existenzprüfung\n    if _, exists := set[2]; exists {\n        fmt.Println("2 ist in der Menge")\n    }\n\n    // Löschen\n    delete(set, 3)\n\n    // Durchlaufen\n    for key := range set {\n        fmt.Println(key)\n    }\n}
\n\n

Vorteile: keine Speicherzuweisung für Werte, Einfachheit. Nachteile: keine Methoden wie .Add() oder .Contains() – alles wird über die map-Syntax erledigt.

\n\n

2. Eigener Typ mit Methoden

\n\n

Wenn Sie eine saubere API benötigen, packen Sie die map in eine Struktur:

\n\n
package main\n\nimport "fmt"\n\n// Set — generische Menge (Go 1.18+)\ntype Set[T comparable] struct {\n    items map[T]struct{}\n}\n\n// NewSet erstellt ein neues Set\nfunc NewSet[T comparable]() *Set[T] {\n    return &Set[T]{items: make(map[T]struct{})}\n}\n\n// Add fügt ein Element hinzu\nfunc (s *Set[T]) Add(item T) {\n    s.items[item] = struct{}{}\n}\n\n// Contains prüft das Vorhandensein\nfunc (s *Set[T]) Contains(item T) bool {\n    _, ok := s.items[item]\n    return ok\n}\n\n// Size gibt die Größe zurück\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

Das ähnelt bereits einer vollwertigen Set-Datenstruktur in Go. Generics gibt es seit Version 1.18, daher funktioniert der Code in modernen Projekten.

\n\n

3. Verwendung von bool anstelle von struct{}

\n\n

Einige verwenden map[T]bool, aber das verbraucht 1 Byte pro Wert. In den meisten Fällen ist der Unterschied unbemerkt, aber wenn Sie einen hochbelasteten Dienst schreiben, sollten Sie besser bei struct{} bleiben.

\n\

Blogs

Buchempfehlungen