In Go ist map ein eingebauter Datentyp, der eine Hashtabelle darstellt.
In anderen Programmiersprachen kennst du eine Map vielleicht als dict, assoziatives Array oder Schlüssel-Wert-Speicher.
Wenn dir dieses Konzept nicht vertraut ist, kannst du dir map wie ein Wörterbuch vorstellen, in dem jedes Wort der key und die Definition das element der Map ist.
Bevor wir anfangen, möchte ich dich auf die Go-Spezifikation für Maps und den Go-Blog für Maps hinweisen, um tiefer in
mapeinzusteigen.
Syntaktisch sieht map so aus:
map[KeyType]ElementType
KeyType muss ein beliebiger vergleichbarer Typ sein, während ElementType jeder gültige Typ in Go sein kann. Das heißt, du kannst alles speichern, von einer primitiven Variable bis zu einem Slice.
Wichtig zu wissen: map in Go ist ungeordnet. Wenn du versuchst, mit einer Schleife durch eine map zu laufen und die Elemente auszugeben, wirst du dich vielleicht wundern, dass deine Elemente in zufälliger Reihenfolge ausgegeben werden (probier es bei Gelegenheit aus).
Ebenso wichtig ist, dass jeder Schlüssel eindeutig ist. Wenn du denselben Schlüssel zweimal zuweist, wird der Wert des entsprechenden Schlüssels überschrieben.
map ist ein Referenztyp. Das bedeutet, wenn du sie weitergibst, kopiert Go nicht die ganze Map.
Stattdessen kopiert Go den Zeiger auf die Map. Dadurch ist es günstig, eine Map an eine Funktion oder Variable zu übergeben.
Der Wert einer nicht initialisierten Map ist nil.
Du kannst eine Map so definieren (man nennt das auch eine nil-Map):
var foo map[string]int
Um eine Map zu initialisieren, kannst du Folgendes tun:
// With map literal
foo := map[string]int{}
oder
// or with make function
foo := make(map[string]int)
Eine nil-Map unterscheidet sich von einer initialisierten Map. In eine nil-Map zu schreiben, führt zu einem Laufzeitfehler
panic: assignment to entry in nil mapDeshalb ist es wichtig, eine Map zu initialisieren, bevor du sie benutzt.
Hier sind einige Operationen, die du mit einer Map ausführen kannst:
// Add a value in a map with the `=` operator:
foo["bar"] = 42
// Here we update the element of `bar`
foo["bar"] = 73
// To retrieve a map value, you can use
baz := foo["bar"]
// To delete an item from a map, you can use
delete(foo, "bar")
Wenn du den Wert für einen Schlüssel abrufst, der nicht in der Map existiert, wird der Nullwert des Werttyps zurückgegeben.
Das kann dich verwirren, besonders wenn der Standardwert deines ElementType (zum Beispiel 0 für einen int) ein gültiger Wert ist.
Um zu prüfen, ob ein Schlüssel in deiner Map existiert, kannst du Folgendes verwenden:
value, exists := foo["baz"]
// If the key "baz" does not exist,
// value: 0; exists: false
Wie wir schon gesehen haben, ist es einfach festzustellen, ob eine map initialisiert ist oder nicht: Wir können sie einfach mit nil vergleichen. Aber was ist mit anderen Maps? Da map nicht vergleichbar ist, können wir den Operator == nicht verwenden. Das Paket reflect hat aber etwas, das uns rettet: DeepEqual. Wir können es so verwenden:
import "reflect"
// ....
equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true
Aber Moment: Wenn eine Map nicht vergleichbar ist, warum können wir sie dann mit nil vergleichen? Nun, die Spezifikation hat dafür eine Ausnahme gemacht, siehe die Spezifikation zu vergleichbaren Typen
Zuletzt: Wenn du versuchst, aus mehreren Goroutinen in eine map zu schreiben, wird der Race Detector ausgelöst, siehe Go Docs und den Go-Blog.
Alternativ kannst du sync.Map, atomic oder mutex verwenden, um dieses Problem zu umgehen.