Slices & Maps
Die zwei wichtigsten Datenstrukturen in Go: Slices als dynamische Listen und Maps als Key-Value-Speicher.
Inhaltsverzeichnis
Slices & Maps
Arrays kennst du - aber im Alltag nutzt kaum jemand Arrays direkt. Go’s praktische Datenstrukturen heissen Slices und Maps.
Slices
Ein Slice ist eine dynamisch groessenveraenderbare Sicht auf ein Array. Du kannst es wie eine normale Liste nutzen:
Slice erstellen
// Literal
namen := []string{"Max", "Anna", "Leo"}
// Leer
leer := []int{}
// Mit Laenge (und Zero Values)
zahlen := make([]int, 5) // [0 0 0 0 0]
// Mit Laenge und Kapazitaet
vorbereitet := make([]int, 0, 100) // leer, aber Platz fuer 100
Zugriff und Laenge
fmt.Println(namen[0]) // Max
fmt.Println(len(namen)) // 3
fmt.Println(cap(namen)) // 3 (Kapazitaet)
append
So waechst ein Slice:
namen = append(namen, "Tim")
namen = append(namen, "Lea", "Jo") // mehrere auf einmal
fmt.Println(namen) // [Max Anna Leo Tim Lea Jo]
Wichtig: append gibt einen neuen Slice zurueck. Deshalb: namen = append(namen, ...).
Hintergrund: Wenn der zugrunde liegende Array nicht mehr reicht, allokiert Go einen neuen (meist doppelt so gross) und kopiert. Das sind die amortisiert O(1)-Kosten, die du von dynamischen Arrays kennst.
Slicen
Ein Slice aus einem Slice:
zahlen := []int{10, 20, 30, 40, 50}
a := zahlen[1:3] // [20 30]
b := zahlen[:2] // [10 20]
c := zahlen[3:] // [40 50]
d := zahlen[:] // gesamtes Slice
Achtung: Die “neuen” Slices teilen sich den gleichen darunterliegenden Speicher. Aenderungen an a[0] aendern auch zahlen[1].
Kopieren
Wenn du eine unabhaengige Kopie willst:
original := []int{1, 2, 3}
kopie := make([]int, len(original))
copy(kopie, original)
Iterieren
for i, name := range namen {
fmt.Printf("%d: %s\n", i, name)
}
Haeufige Operationen
Element entfernen (Index i):
slice = append(slice[:i], slice[i+1:]...)
Element einfuegen (an Index i):
slice = append(slice[:i], append([]int{wert}, slice[i:]...)...)
Nicht elegant, aber funktional. In Go 1.21+ gibt es slices.Delete und slices.Insert im slices-Package.
Das slices-Package (Go 1.21+)
import "slices"
zahlen := []int{3, 1, 4, 1, 5, 9, 2, 6}
slices.Sort(zahlen) // sortieren
vorhanden := slices.Contains(zahlen, 4) // true
index := slices.Index(zahlen, 5) // 6
zahlen = slices.Delete(zahlen, 0, 2) // Indizes 0-1 entfernen
Maps
Maps sind Key-Value-Speicher (in anderen Sprachen: Dictionary, HashMap):
Map erstellen
// Literal
alter := map[string]int{
"Anna": 28,
"Max": 34,
"Leo": 22,
}
// Leer
leer := map[string]int{}
// Mit make
punkte := make(map[string]int)
Zugriff
fmt.Println(alter["Anna"]) // 28
// Schreiben / Hinzufuegen
alter["Tim"] = 41
// Loeschen
delete(alter, "Max")
// Laenge
fmt.Println(len(alter))
Key existiert?
Eine der wichtigsten Go-Idiome - das comma-ok-Pattern:
wert, ok := alter["Unbekannt"]
if ok {
fmt.Println("Da:", wert)
} else {
fmt.Println("Nicht gefunden")
}
Warum? Maps geben bei fehlendem Key den Zero Value zurueck - du koenntest also nicht zwischen “existiert, Wert 0” und “existiert nicht” unterscheiden. Das zweite Returnwert ok loest das.
Iterieren
for name, jahre := range alter {
fmt.Printf("%s: %d\n", name, jahre)
}
Achtung: Die Reihenfolge ist zufaellig. Wenn du Ordnung brauchst, sortiere die Keys:
import (
"fmt"
"sort"
)
keys := make([]string, 0, len(alter))
for k := range alter {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf("%s: %d\n", k, alter[k])
}
Maps von komplexeren Typen
Du kannst fast alles als Wert haben:
type Person struct {
Name string
Alter int
}
leute := map[string]Person{
"anna": {Name: "Anna", Alter: 28},
"max": {Name: "Max", Alter: 34},
}
Vergleich Arrays vs. Slices vs. Maps
| Eigenschaft | Array | Slice | Map |
|---|---|---|---|
| Groesse | fest | dynamisch | dynamisch |
| Key-Typ | int (Index) | int (Index) | beliebig (hashbar) |
| Reihenfolge | geordnet | geordnet | zufaellig |
| Haeufigkeit | selten | sehr haeufig | haeufig |
Praxistipp: Fang mit Slices an, nimm Maps wenn Lookups nach nicht-numerischen Keys noetig sind, Arrays nur wenn du wirklich eine feste Groesse brauchst.
Praktisches Beispiel
package main
import (
"fmt"
"strings"
)
func zaehleWoerter(text string) map[string]int {
ergebnis := make(map[string]int)
for _, wort := range strings.Fields(text) {
ergebnis[strings.ToLower(wort)]++
}
return ergebnis
}
func main() {
text := "Go ist schnell und Go ist einfach"
for wort, n := range zaehleWoerter(text) {
fmt.Printf("%s: %d\n", wort, n)
}
}
Zusammenfassung
- Slices: dynamische Listen - dein Standard-Tool
- Maps: Key-Value-Speicher - Reihenfolge ist zufaellig
append,len,cap,copysind die wichtigsten Slice-Builtinsmakeerstellt Slices/Maps mit Groessen-Hints- Comma-ok (
wert, ok := m[key]) fuer existenzpruefungen - Das
slices-Package (Go 1.21+) macht vieles bequemer
Im naechsten Kapitel: Goroutines und Channels - Go’s Paradedisziplin fuer nebenlaeufigen Code.