Stufe 5Collections · Lektion 27 von 34
Map: Schlüssel und Werte
Eine Map ordnet Schlüsseln Werte zu – Ländercodes zu Namen, Artikelnummern zu Preisen, Wörtern zu Häufigkeiten. In Scala ist sie unveränderlich, liefert fehlende Einträge als Option und lässt sich mit denselben Methoden auswerten wie Listen.
Maps erzeugen
val laender = Map(
"de" -> "Deutschland",
"fr" -> "Frankreich",
"it" -> "Italien"
)"de" -> "Deutschland" ist nur eine andere Schreibweise für das Tupel ("de", "Deutschland"). Eine Map ist also eine Sammlung von Paaren, bei der jeder Schlüssel nur einmal vorkommt. Kommt ein Schlüssel doppelt vor, gewinnt der letzte Eintrag.
Werte abfragen
laender("de") // "Deutschland"
laender("es") // NoSuchElementException!
laender.get("es") // None
laender.get("fr") // Some("Frankreich")
laender.getOrElse("es", "unbekannt") // "unbekannt"
laender.contains("it") // trueDer direkte Zugriff mit Klammern wirft bei fehlendem Schlüssel eine Ausnahme. get liefert stattdessen eine Option und zwingt Sie, den Fall „nicht vorhanden“ zu bedenken. Für einen festen Ersatzwert gibt es withDefaultValue:
val punkte = Map("Ada" -> 12).withDefaultValue(0)
punkte("Alan") // 0Einträge ändern
Maps sind unveränderlich; jede Änderung liefert eine neue Map:
val mehr = laender + ("es" -> "Spanien")
val anders = laender.updated("it", "Italia")
val weniger = laender - "fr" // oder laender.removed("fr")
val beide = laender ++ Map("at" -> "Österreich", "ch" -> "Schweiz")Für einen Wert, der aus dem bisherigen berechnet wird, eignet sich updatedWith:
val lager = Map("Schraube" -> 120, "Mutter" -> 80)
lager.updatedWith("Schraube")(_.map(_ - 20)) // Schraube -> 100Durchlaufen und umwandeln
for (code, name) <- laender do println(s"$code: $name")
laender.keys // Iterable(de, fr, it)
laender.values // Iterable(Deutschland, Frankreich, Italien)
laender.map((k, v) => k.toUpperCase -> v.length) // Map(DE -> 11, FR -> 10, IT -> 7)
laender.filter((_, v) => v.startsWith("F")) // Map(fr -> Frankreich)
laender.view.mapValues(_.toUpperCase).toMap // nur die Werte umwandelnIn Scala 3 dürfen Lambdas über Paare direkt zwei Parameter haben – (k, v) => … statt { case (k, v) => … }.
Maps aus Listen bauen
Oft entsteht eine Map aus einer Liste:
case class Produkt(nummer: String, name: String, preis: BigDecimal)
val produkte = List(
Produkt("A-1", "Tasse", 8),
Produkt("A-2", "Teller", 12),
Produkt("B-7", "Kanne", 29)
)
val nachNummer: Map[String, Produkt] = produkte.map(p => p.nummer -> p).toMap
nachNummer("B-7").name // "Kanne"
val nachSerie = produkte.groupBy(_.nummer.take(1))
// Map(A -> List(Tasse, Teller), B -> List(Kanne))
val summeJeSerie = produkte.groupMapReduce(_.nummer.take(1))(_.preis)(_ + _)
// Map(A -> 20, B -> 29)groupMapReduce gruppiert, wandelt um und fasst zusammen – in einem Schritt. Klassisches Beispiel ist das Zählen von Wörtern:
val text = "die treppe hat stufen und die stufen haben kanten"
text.split(" ").toList.groupMapReduce(identity)(_ => 1)(_ + _)
// Map(die -> 2, stufen -> 2, treppe -> 1, …)Reihenfolge
Wie beim Set ist die Reihenfolge einer normalen Map nicht festgelegt. Sortierte Schlüssel liefert SortedMap (intern ein TreeMap), die Einfügereihenfolge bewahrt ListMap bzw. VectorMap:
import scala.collection.immutable.{SortedMap, VectorMap}
SortedMap("c" -> 3, "a" -> 1, "b" -> 2) // TreeMap(a -> 1, b -> 2, c -> 3)
VectorMap("c" -> 3, "a" -> 1) // Reihenfolge wie eingefügtVeränderliche Maps
Für Zähler, Caches oder schrittweise aufgebaute Indizes gibt es mutable.Map:
import scala.collection.mutable
val zaehler = mutable.Map.empty[String, Int].withDefaultValue(0)
for w <- List("a", "b", "a") do zaehler(w) += 1
zaehler // HashMap(a -> 2, b -> 1)
val cache = mutable.Map.empty[Int, BigInt]
def teuer(n: Int): BigInt = cache.getOrElseUpdate(n, BigInt(2).pow(n))getOrElseUpdate berechnet einen Wert nur beim ersten Mal und speichert ihn – eine einfache Form von Memoisierung.