Zum Inhalt springen

HashMap in Rust

Eine HashMap speichert Daten als Schlüssel-Wert-Paare. Du schlägst etwas also über einen Schlüssel nach – nicht über eine Position wie bei einem Array oder Vektor. Wenn du in Python schon ein Dictionary oder in JavaScript ein Objekt als Map benutzt hast: Das ist dieselbe Idee.

use std::collections::HashMap;
fn main() {
let mut ages = HashMap::new();
ages.insert("Alice", 30);
ages.insert("Bob", 25);
println!("{:?}", ages);
}

use std::collections::HashMap; ganz oben holt den Typ in den Gültigkeitsbereich (Scope). Anders als Vec und String ist HashMap nicht automatisch verfügbar – diese Zeile brauchst du also.

▶ Selbst ausprobieren
use std::collections::HashMap;
fn main() {
let mut ages = HashMap::new();
ages.insert("Alice", 30);
match ages.get("Alice") {
Some(age) => println!("Alice ist {age}"),
None => println!("Nicht gefunden"),
}
}
▶ Selbst ausprobieren

.get() gibt nicht direkt den Wert zurück, sondern eine Option – denn den Schlüssel gibt es vielleicht gar nicht. Option bekommt noch eine eigene Seite. Bis dahin ist match mit Some/None der übliche Weg, damit umzugehen.

Fügst du denselben Schlüssel noch einmal ein, wird der alte Wert überschrieben:

use std::collections::HashMap;
fn main() {
let mut ages = HashMap::new();
ages.insert("Alice", 30);
ages.insert("Alice", 31); // überschreibt 30
println!("{:?}", ages.get("Alice"));
}
▶ Selbst ausprobieren

Willst du einen Wert nur dann einfügen, wenn es den Schlüssel noch nicht gibt, nimm .entry():

use std::collections::HashMap;
fn main() {
let mut ages = HashMap::new();
ages.insert("Alice", 30);
ages.entry("Alice").or_insert(99); // Alice gibt es schon, also passiert nichts
ages.entry("Bob").or_insert(25); // Bob gibt es noch nicht, also wird er eingefügt
println!("{:?}", ages);
}
▶ Selbst ausprobieren
use std::collections::HashMap;
fn main() {
let mut ages = HashMap::new();
ages.insert("Alice", 30);
ages.insert("Bob", 25);
for (name, age) in &ages {
println!("{name} ist {age}");
}
}
▶ Selbst ausprobieren

Eine HashMap ist immer dann richtig, wenn du Dinge über einen Namen oder eine ID nachschlagen willst statt über eine Position: Wörter zählen, Benutzernamen ihren Daten zuordnen, Ergebnisse unter einem Schlüssel zwischenspeichern. Ist die Reihenfolge wichtig oder arbeitest du einfach eine Liste ab, passt ein Vec besser.

Speichere drei Namen mit Telefonnummern in einer HashMap. Schlag dann "Ben" nach (der drinsteht) und "Dana" (die nicht drinsteht), und gib für beide eine freundliche Zeile aus. Tipp: Lauf über ["Ben", "Dana"] und nimm ein match auf .get(name).

Lösung anzeigen
use std::collections::HashMap;
fn main() {
let mut phone_book = HashMap::new();
phone_book.insert("Ana", "555-1234");
phone_book.insert("Ben", "555-9876");
phone_book.insert("Chen", "555-4321");
for name in ["Ben", "Dana"] {
match phone_book.get(name) {
Some(number) => println!("{name}: {number}"),
None => println!("{name}: nicht im Telefonbuch"),
}
}
}

Ausgabe:

Ben: 555-9876
Dana: nicht im Telefonbuch
▶ Selbst ausprobieren

Weil wir über die gesuchten Namen laufen statt über die Map, bekommen wir außerdem eine feste Reihenfolge. Das ist ein praktischer Trick, wann immer du von einer HashMap eine vorhersagbare Ausgabe brauchst.

Du hast die Stimmen ["rot", "blau", "rot", "grün", "blau", "rot"]. Zähle, wie viele jede Farbe bekommen hat, und gib dann die Ergebnisse für "rot", "grün", "blau" und "rosa" aus (die keine bekommen hat). Tipp: counts.entry(color).or_insert(0) liefert dir den aktuellen Zählerstand dieser Farbe, beginnend bei 0. Mit *count += 1 erhöhst du ihn um eins.

Lösung anzeigen
use std::collections::HashMap;
fn main() {
let votes = ["rot", "blau", "rot", "grün", "blau", "rot"];
let mut counts = HashMap::new();
for color in votes {
let count = counts.entry(color).or_insert(0);
*count += 1;
}
for color in ["rot", "grün", "blau", "rosa"] {
match counts.get(color) {
Some(n) => println!("{color}: {n}"),
None => println!("{color}: 0"),
}
}
}

Ausgabe:

rot: 3
grün: 1
blau: 2
rosa: 0
▶ Selbst ausprobieren

or_insert(0) gibt eine veränderbare Referenz auf den Wert in der Map zurück, keine Kopie. Das * in *count += 1 bedeutet „ändere die Zahl, auf die das hier zeigt“ – so wird die Map selbst aktualisiert. Dieses Muster „entry, sonst 0 einfügen, dann 1 addieren“ ist in Rust der Standardweg, um Dinge zu zählen.

  • HashMap<K, V> speichert Schlüssel-Wert-Paare. Du holst sie mit use std::collections::HashMap; in den Scope.
  • .insert(key, value) fügt ein oder überschreibt. .get(key) schlägt nach und gibt eine Option zurück.
  • .entry(key).or_insert(value) fügt nur ein, wenn es den Schlüssel noch nicht gibt.
  • Die Reihenfolge beim Durchlaufen ist nicht garantiert – verlass dich nicht darauf.

Kurzer Check

1. Was brauchst du – anders als bei Vec oder String –, um HashMap in den Scope zu holen?

2. Was macht .entry(key).or_insert(value)?

3. Kannst du dich beim Durchlaufen einer HashMap auf die Reihenfolge der Einträge verlassen?

Punkte: 0 / 3