EMZETT.
Login

Kurz: Ein Array hat eine feste Länge (Teil des Typs). In der Praxis nutzt man fast immer Slices: dynamische Sichten auf Arrays.

Teil des Kurses Go

Kapitel 5 von 11 im Kurs Go (Abschnitt „Datenstrukturen“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Arrays und Slices

Ein Array hat eine feste Länge (Teil des Typs). In der Praxis nutzt man fast immer Slices: dynamische Sichten auf Arrays.

package main
 
import "fmt"
 
func main() {
    var a [3]int // Array: feste Länge, Nullwerte
    a[0] = 10
    fmt.Println(a, len(a))
 
    s := []int{5, 3, 9} // Slice: wachsend
    s = append(s, 1, 7)
    fmt.Println(s, len(s), cap(s))
    fmt.Println(s[1:3], s[:2], s[2:], s[len(s)-1])
 
    leer := make([]int, 3, 10) // Länge 3, Kapazität 10
    fmt.Println(leer, len(leer), cap(leer))
 
    var nichts []int // nil-Slice: len 0, funktioniert mit append
    fmt.Println(nichts == nil, len(nichts))
    nichts = append(nichts, 1)
    fmt.Println(nichts)
 
    kopie := make([]int, len(s))
    n := copy(kopie, s)
    kopie[0] = 99
    fmt.Println(n, s[0], kopie[0])
}

Ausgabe:

[10 0 0] 3
[5 3 9 1 7] 5 6
[3 9] [5 3] [9 1 7] 7
[0 0 0] 3 10
true 0
[1]
5 5 99
OperationBedeutung
s[i]Zugriff (außerhalb → Panic)
s[a:b]Teil-Slice von a bis vor b (teilt den Speicher!)
append(s, x, y)anhängen (kann neu allokieren)
len(s), cap(s)Länge, Kapazität
copy(ziel, quelle)kopieren
make([]T, n, c)anlegen

Achtung

Ein Teil-Slice teilt sich den Speicher mit dem Original. Änderungen wirken auf beide. Mit append auf einen Teil-Slice kann man Daten des Originals überschreiben.

package main
 
import "fmt"
 
func main() {
    original := []int{1, 2, 3, 4, 5}
    teil := original[1:3]
    teil[0] = 99
    fmt.Println(original, teil)
    teil = append(teil, 100) // überschreibt original[3]!
    fmt.Println(original, teil)
 
    sicher := append([]int(nil), original[1:3]...) // echte Kopie
    sicher[0] = -1
    fmt.Println(original[1], sicher[0])
}

Ausgabe:

[1 99 3 4 5] [99 3]
[1 99 3 100 5] [99 3 100]
99 -1

Slices bearbeiten

package main
 
import (
    "fmt"
    "slices"
    "sort"
)
 
func main() {
    z := []int{5, 3, 9, 1, 7}
    slices.Sort(z)
    fmt.Println(z, slices.Contains(z, 9), slices.Index(z, 7), slices.Max(z))
    i, gefunden := slices.BinarySearch(z, 5)
    fmt.Println(i, gefunden)
 
    z = slices.Delete(z, 1, 3) // Elemente 1 und 2 löschen
    fmt.Println(z)
    z = slices.Insert(z, 1, 42)
    fmt.Println(z)
 
    namen := []string{"Zoe", "anna", "Ben"}
    sort.Slice(namen, func(i, j int) bool { return len(namen[i]) < len(namen[j]) })
    fmt.Println(namen)
    slices.SortFunc(namen, func(a, b string) int { return len(b) - len(a) })
    fmt.Println(namen)
 
    matrix := [][]int{{1, 2}, {3, 4}}
    fmt.Println(matrix[1][0], len(matrix))
}

Ausgabe:

[1 3 5 7 9] true 3 9
2 true
[1 7 9]
[1 42 7 9]
[Zoe Ben anna]
[anna Zoe Ben]
3 2

Maps

Eine Map ordnet Schlüssel Werten zu:

package main
 
import (
    "fmt"
    "maps"
    "slices"
    "sort"
)
 
func main() {
    alter := map[string]int{"Mia": 17, "Tom": 19}
    alter["Zoe"] = 21
    alter["Mia"] = 18
    fmt.Println(alter["Mia"], alter["Unbekannt"], len(alter))
 
    wert, vorhanden := alter["Max"] // "comma ok": unterscheidet 0 von "nicht da"
    fmt.Println(wert, vorhanden)
    if a, ok := alter["Zoe"]; ok {
        fmt.Println("Zoe ist", a)
    }
    delete(alter, "Tom")
 
    // Reihenfolge ist zufällig: für feste Ausgabe Schlüssel sortieren
    schluessel := make([]string, 0, len(alter))
    for k := range alter {
        schluessel = append(schluessel, k)
    }
    sort.Strings(schluessel)
    for _, k := range schluessel {
        fmt.Print(k, "=", alter[k], " ")
    }
    fmt.Println()
    fmt.Println(slices.Sorted(maps.Keys(alter)))
 
    // Wörter zählen
    zaehler := map[string]int{}
    for _, w := range []string{"das", "ist", "das", "beste"} {
        zaehler[w]++
    }
    fmt.Println(zaehler)
 
    var nilMap map[string]int // nil-Map: Lesen ok, Schreiben = Panic
    fmt.Println(nilMap["x"], len(nilMap))
 
    gruppen := map[bool][]int{}
    for _, n := range []int{1, 2, 3, 4, 5} {
        gruppen[n%2 == 0] = append(gruppen[n%2 == 0], n)
    }
    fmt.Println(gruppen)
}

Ausgabe:

18 0 3
0 false
Zoe ist 21
Mia=18 Zoe=21
[Mia Zoe]
map[beste:1 das:2 ist:1]
0 0
map[false:[1 3 5] true:[2 4]]

Die Iterationsreihenfolge einer Map ist absichtlich zufällig. fmt.Println auf einer Map gibt sie dagegen sortiert aus. Maps sind nicht threadsicher: Bei gleichzeitigem Zugriff brauchst du einen sync.Mutex oder sync.Map.

Mengen mit Maps

package main
 
import "fmt"
 
func main() {
    menge := map[string]struct{}{} // struct{} belegt keinen Speicher
    for _, w := range []string{"a", "b", "a", "c", "b"} {
        menge[w] = struct{}{}
    }
    _, drin := menge["a"]
    fmt.Println(len(menge), drin)
}

Ausgabe:

3 true

Merke

  • Arrays haben feste Länge; Slices ([]T) wachsen mit append und sind Sichten auf Arrays
  • len, cap, copy, make; Teil-Slices teilen sich den Speicher
  • Maps: m[k], v, ok := m[k], delete; Reihenfolge ist zufällig
  • Pakete slices und maps (Go 1.21) bieten Sortieren, Suchen, Einfügen
  • Eine nil-Map nimmt keine Schreibzugriffe an: vorher make

Übungsaufgabe

Zähle, wie oft jeder Buchstabe in einem Text vorkommt, und gib die Buchstaben alphabetisch mit Anzahl aus.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Funktionen und Fehlerbehandlung

Weiter: Structs, Methoden und Zeiger

Alle Kapitel: Go im Überblick