Zum Inhalt springen
Go Anfänger 30 min

Slices & Maps

Die zwei wichtigsten Datenstrukturen in Go: Slices als dynamische Listen und Maps als Key-Value-Speicher.

Aktualisiert:
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

EigenschaftArraySliceMap
Groessefestdynamischdynamisch
Key-Typint (Index)int (Index)beliebig (hashbar)
Reihenfolgegeordnetgeordnetzufaellig
Haeufigkeitseltensehr haeufighaeufig

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, copy sind die wichtigsten Slice-Builtins
  • make erstellt 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.

Zurück zum Go Kurs