Go enthält einen nativen Typ namens Map, der Hash-Tabellen implementiert. Es handelt sich um einen Datentyp, der aus einer Sammlung eindeutiger Schlüssel und einer Sammlung von Werten für jeden dieser Schlüssel besteht.
Es kann beispielsweise mit einem Wörterbuch in anderen Sprachen verglichen werden, das Schlüssel-Wert-Paare speichert. Auf diese Werte wird mithilfe von Schlüsseln zugegriffen, genau wie bei Arrays und Slices, wie wir im vorherigen Beitrag gesehen haben.
Die Indizes sind nicht auf eine Zahl beschränkt wie in Arrays oder Slices und die Elemente sind nicht geordnet. Wenn wir also eine Karte drucken, wird sie eine zufällige Reihenfolge zurückgeben, wenn wir nichts unternehmen, um das Drucken zu überschreiben und die gewünschte Reihenfolge zu erzwingen.
Um eine Karte zu deklarieren, erfolgt dies mit dem Wert „map[key]“, wobei „key“ der Typ ist, den unser Schlüssel haben soll (er muss von einem vergleichbaren Typ sein https://go.dev/ref/spec#Comparison_operators ) und value wird der Typ sein, in dem die Karte in jedem der Schlüssel gespeichert werden soll, egal um welchen Typ es sich handelt, von einem int zu einer Struktur oder einer anderen Karte, was auch immer wir wollen.
Wie bei Slices sind Karten referenzierte Typen, was bedeutet, dass der Nullwert einer Karte Null ist.
Dies geschieht, weil sich darunter eine Hash-Tabelle befindet, in der die Schlüssel und Werte gespeichert sind, und sie lediglich eine Hülle, eine Abstraktion davon sind.
Wenn wir es als Folgendes deklarieren:
var m map[int]int
sein Wert wird Null sein.
Wenn wir möchten, dass es einen Nullwert hat, können wir die Deklaration verwenden:
m := map[int]int{}
Und wir können es sogar genau wie die Slices initialisieren, indem wir die Make-Funktion verwenden.
m := make(map[string]string)
Dadurch wird eine Hash-Map mit dem entsprechenden Speicherpool initialisiert und somit eine Karte zurückgegeben, die auf diese Datenstruktur verweist.
Das Hinzufügen von Werten zu einer Karte erfolgt durch die Verwendung von geschweiften Klammern [] und der geschweiften Klammer, genau wie bei Arrays oder Slices. In diesem Beispiel erstellen wir eine Karte mit den Schlüsseln als Zeichenfolgen und den Werten als Ganzzahlen, um Namen und Alter zu speichern.
ages := make(map[string]int) ages["John"] = 33 ages["Charly"] = 27 ages["Jenny"] = 45 ages["Lisa"] = 19
Wenn wir die Werte hinzufügen möchten, wenn wir die Karte deklarieren, können wir die Kurzdeklaration verwenden und alles im selben Schritt erledigen:
ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
Um die Werte zu lesen, müssen wir lediglich den Schlüssel zu unserer Karte angeben und sie wird diesen Wert zurückgeben. Um zum Beispiel Lisas Alter herauszufinden, können wir Folgendes tun:
fmt.Println(ages["Lisa"]) // 19
Wenn wir versuchen, auf einen Schlüssel zuzugreifen, der nicht existiert, ist der erhaltene Wert der Nullwert des Typs, in diesem Fall wäre es „“, da es sich um eine Zeichenfolge handelt.
Um zu überprüfen, ob ein Element in der Karte vorhanden ist, können wir prüfen, ob der Typ der Standardtyp ist. Dies ist jedoch nicht sehr zuverlässig, da es möglicherweise vorhanden ist, sein Wert jedoch ein leerer String oder 0 im Fall von int ist , was mit seinem Nullwert übereinstimmen würde, also hilft uns Go bei Folgendem:
val, ok := ages["Randy"]
Wenn wir die Karte mit zwei Werten gleichsetzen, ist der erste der Wert des Elements, auf das über den Schlüssel zugegriffen wird, in diesem Fall „Randy“, der nicht existiert, und der zweite ist ein boolescher Wert, der angibt, ob dies der Fall ist existiert oder nicht.
Wenn uns der Wert nicht interessiert und wir lediglich prüfen möchten, ob ein Schlüssel vorhanden ist, können wir den Wert mit _ wie folgt ignorieren:
_, ok := ages["Randy"]
Wie bei Arrays und Slices können wir die len-Funktion verwenden, um herauszufinden, wie viele Elemente es in der Karte gibt.
fmt.Println(len(ages)) // 4
Wenn wir einen Wert ändern möchten, müssen wir lediglich mit einem Schlüssel auf den Wert zugreifen und ihn mit einem anderen abgleichen, und schon wird er geändert.
Wenn wir eine zweite Karte deklarieren, die auf die erste zeigt, und wir den Wert der zweiten Karte ändern, da es sich um einen referenzierten Typ handelt, ändern wir den Wert der ersten, da beide die gleiche Hash-Tabelle darunter verwenden.
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]
Um Elemente aus einer Karte zu löschen, stellt uns Go eine Löschfunktion mit der folgenden Signatur delete(m map[Type]Type1, key Type) zur Verfügung, die eine Karte und den zu löschenden Schlüssel empfängt.
Wenn wir im vorherigen Fall „Lisa“ eliminieren wollten, würden wir Folgendes tun:
delete(ages, "Lisa")
Wenn wir den Inhalt einer Karte durchgehen möchten, können wir dies mit einem for mit der Variation des Bereichs tun, die wir bereits im Beitrag zu Arrays und Slices gesehen haben.
Dann ist das erste Element der Index, also der Schlüssel, und das zweite der Wert.
for key, value := range ages { fmt.Printf("%s: %d\n", key, value) } // Output: // Jenny: 45 // Lisa: 19 // John: 33 // Charly: 27
Wenn uns wie bei Arrays und Slices nur der Wert interessiert, ohne den Schlüssel, können wir ihn weglassen, indem wir _ verwenden.
for _, value := range ages { fmt.Println(value) } // Output: // 19 // 33 // 27 // 45
Und wenn uns nur der Schlüssel interessiert, können wir den Bereich einer einzelnen Variablen zuweisen, um ihn zu erhalten:
for key := range ages { fmt.Println(key) } // Output: // John // Charly // Jenny // Lisa
Wie ich in der Einleitung erwähnt habe, sind die Informationen in einer Karte nicht geordnet. Wenn wir sie also durchlaufen, können wir weder angeben, welcher Reihenfolge sie folgen, noch kann Go garantieren, dass die Reihenfolge zwischen den Ausführungen dieselbe ist.
Wie wir bei Arrays und Slices gesehen haben, gibt es in der Standardbibliothek ein Sortierpaket, das uns beim Sortieren von Elementen hilft: https://pkg.go.dev/sort
In Anlehnung an unser Beispiel mit Alter und mithilfe von Sortieren können wir die Schlüssel der Karte sortieren, bevor wir sie durchqueren, und so garantieren, dass auf sie in der richtigen Reihenfolge zugegriffen wird.
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
Wir deklarieren unsere Alterskarte mit der Kurzdeklaration, wie wir zuvor gesehen haben.
Wir erstellen String-Slices zum Speichern der Schlüssel und verwenden die make-Methode mit der Länge 0, da wir im Moment keine Schlüssel haben, reservieren aber die Kapazität, die sie haben wird, mithilfe der len-Methode für die Länge unserer Karte.
Wir gehen die Alterskarte durch, um ihre Schlüssel zu behalten und sie dem erstellten Slice hinzuzufügen.
Wir sortieren die Schlüssel alphabetisch mit der Funktion sort.Strings.
Wir gehen den bereits bestellten Schlüsselabschnitt durch und greifen mit dem betreffenden Schlüssel auf die Karte zu.
Auf diese Weise greifen wir geordnet auf die Karte zu und können die Logik ausführen, die unser Programm benötigt.
Bei Karten ist zu beachten, dass die gleichzeitige Verwendung nicht sicher ist. Wenn es sich um gleichzeitige Lesevorgänge handelt, bei denen entweder auf einen Wert oder über ein for mit einem Bereich zugegriffen wird, besteht kein Problem, wenn mehrere Goroutinen gleichzeitig darauf zugreifen.
Der problematische Fall liegt vor, wenn Sie den Wert einer Karte gleichzeitig aktualisieren möchten, indem Sie Elemente hinzufügen oder daraus entfernen, und ihn gleichzeitig beispielsweise von einer anderen Seite lesen.
Um diese Situation zu lösen, gibt es mehrere mögliche Lösungen, auf die ich nicht näher eingehen werde. Ich werde sie lediglich erwähnen und es Ihrer Entscheidung überlassen, näher darauf einzugehen.
Wenn wir das Synchronisierungspaket https://pkg.go.dev/sync aus der Standardbibliothek verwenden, können wir die Synchronität zwischen den verschiedenen Goroutinen steuern.
Eine mögliche Verwendung ist der RWMutex-Typ, der es uns ermöglicht, Lese- und Schreibvorgänge für einen Typ zu sperren und zu entsperren. Wenn wir also einen Typ haben, der einen sync.RWMutex und eine Map enthält, können wir steuern, wann darauf zugegriffen werden kann.
Ein weiterer interessanter Typ, den es innerhalb desselben Synchronisierungspakets zu untersuchen gilt, ist Map, das uns bereits eine Reihe von Funktionen bietet, die uns bei der Arbeit mit unserer Map helfen, mit der wir letztendlich nicht nativ arbeiten können, wie bei der vorherigen Lösung.
Abhängig von dem Anwendungsfall, den wir implementieren, wird das eine oder das andere für uns nützlicher sein, und es gibt niemanden, der besser ist als der andere, es wird immer davon abhängen, was wir brauchen.
Ich hoffe, dass alles, was ich in diesem Beitrag zu erklären versucht habe, klar war, und wenn es einen Teil gibt, der nicht ganz klar war, oder Teile, die ich nicht behandelt habe und die Sie möchten, dass ich sie mache, lassen Sie es bitte Schreiben Sie mir hier oder über meine sozialen Netzwerke einen Kommentar zu Ihrem Profil und ich werde gerne antworten.
Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.
Copyright© 2022 湘ICP备2022001581号-3