Map und Set in JavaScript: Die unterschätzten Datenstrukturen

Objekte und Arrays sind nicht immer die beste Wahl. Lerne, wann Map und Set deinen Code sauberer, schneller und zuverlässiger machen.

Teilen

Die meisten von uns greifen für so ziemlich alles zu {} und []. Objekte für Schlüssel-Wert-Paare, Arrays für Listen – das reicht doch. Meistens stimmt das auch. Aber JavaScript kennt seit ES2015 zwei weitere Datenstrukturen, die viele Probleme eleganter lösen: Map und Set. In diesem Beitrag schauen wir uns an, was sie können, wann sie klar die bessere Wahl sind und wo ihre Grenzen liegen.

Warum nicht einfach Objekte und Arrays?

Objekte funktionieren gut als Nachschlagewerk, haben aber Tücken. Ihre Schlüssel sind immer Strings (oder Symbols) – nutzt du eine Zahl als Schlüssel, wird sie still in einen String umgewandelt. Außerdem erbt jedes Objekt Eigenschaften wie toString, was zu überraschenden Kollisionen führen kann. Und die Anzahl der Einträge zu ermitteln ist umständlich.

const obj = {};
obj[1] = "eins";
obj["1"] = "auch eins?";
console.log(obj[1]); // "auch eins?" – die Zahl 1 und der String "1" kollidieren

console.log("toString" in obj); // true, obwohl wir das nie gesetzt haben
console.log(Object.keys(obj).length); // umständlicher Umweg, um die Größe zu erfahren

Genau hier setzt die Map an: Sie speichert echte Schlüssel-Wert-Paare, bei denen der Schlüssel jeden beliebigen Typ haben darf – auch Zahlen, Objekte oder Funktionen.

Die Map: Schlüssel-Wert-Paare mit Superkräften

Eine Map erstellst du mit new Map(). Statt eckiger Klammern nutzt du die Methoden set, get, has und delete. Die aktuelle Größe liest du direkt über die Eigenschaft size ab.

const nutzer = new Map();

const key = { id: 42 };        // ein Objekt als Schlüssel – bei Objekten unmöglich
nutzer.set(key, "Anna");
nutzer.set(1, "Zahl-Schlüssel");
nutzer.set("1", "String-Schlüssel"); // kollidiert NICHT mit der Zahl 1

console.log(nutzer.get(key));   // "Anna"
console.log(nutzer.get(1));     // "Zahl-Schlüssel"
console.log(nutzer.has("1"));   // true
console.log(nutzer.size);       // 3

nutzer.delete(1);
console.log(nutzer.size);       // 2

Ein wichtiger Vorteil nebenbei: Eine Map merkt sich die Reihenfolge, in der Einträge eingefügt wurden. Bei Objekten ist die Reihenfolge zwar heute meist stabil, aber die Regeln dazu sind knifflig – die Map macht daraus eine klare Zusage.

Über Maps iterieren

Maps sind von Haus aus iterierbar. Am elegantesten kombinierst du eine for...of-Schleife mit Destructuring, um Schlüssel und Wert direkt zu entpacken. Alternativ liefern keys(), values() und entries() die passenden Teile.

const preise = new Map([
  ["Apfel", 0.5],
  ["Banane", 0.3],
  ["Kirsche", 1.2],
]);

for (const [frucht, preis] of preise) {
  console.log(`${frucht}: ${preis} €`);
}

// Nur die Schlüssel
console.log([...preise.keys()]); // ["Apfel", "Banane", "Kirsche"]

// Summe aller Werte
let summe = 0;
for (const preis of preise.values()) {
  summe += preis;
}
console.log(summe); // 2

Beachte, dass du eine Map mit einem Array aus [key, value]-Paaren initialisieren kannst. Umgekehrt kommst du mit Object.fromEntries(map) jederzeit zurück zu einem normalen Objekt, falls du das etwa für JSON.stringify brauchst – denn Maps selbst werden von JSON.stringify nicht serialisiert.

Das Set: Sammlung eindeutiger Werte

Ein Set ist eine Sammlung, in der jeder Wert nur einmal vorkommen kann. Fügst du einen bereits vorhandenen Wert erneut hinzu, passiert einfach nichts. Das macht Sets ideal für Aufgaben, bei denen es auf Eindeutigkeit ankommt.

const tags = new Set();
tags.add("javascript");
tags.add("css");
tags.add("javascript"); // Duplikat – wird ignoriert

console.log(tags.size);        // 2
console.log(tags.has("css"));  // true
tags.delete("css");
console.log([...tags]);        // ["javascript"]

Die Prüfung mit has ist bei einem Set im Schnitt deutlich schneller als ein includes auf einem großen Array, weil das Array im schlimmsten Fall jedes Element durchgehen muss. Wenn du oft prüfst, ob ein Wert bereits vorhanden ist, ist ein Set fast immer die bessere Wahl.

Praxisbeispiel: Duplikate entfernen und zählen

Der wohl bekannteste Einzeiler mit Sets: Duplikate aus einem Array entfernen. Weil ein Set iterierbar ist, verwandelt der Spread-Operator es direkt wieder in ein Array.

const zahlen = [1, 2, 2, 3, 3, 3, 4];
const einzigartig = [...new Set(zahlen)];
console.log(einzigartig); // [1, 2, 3, 4]

Kombiniert man beide Strukturen, wird es richtig praktisch. Hier zählen wir, wie oft jedes Wort in einem Text vorkommt – eine klassische Aufgabe, die mit einer Map sauber gelingt:

function zaehleWoerter(text) {
  const haeufigkeit = new Map();
  for (const wort of text.toLowerCase().split(/\s+/)) {
    haeufigkeit.set(wort, (haeufigkeit.get(wort) ?? 0) + 1);
  }
  return haeufigkeit;
}

const ergebnis = zaehleWoerter("der Hund und der Kater und der Vogel");
console.log(ergebnis.get("der")); // 3
console.log(ergebnis.get("und")); // 2

Der Ausdruck haeufigkeit.get(wort) ?? 0 nutzt den Nullish-Coalescing-Operator: Existiert der Schlüssel noch nicht, liefert get den Wert undefined, und wir starten sauber bei 0.

WeakMap und WeakSet: die spezialisierten Geschwister

Kurz erwähnt seien noch WeakMap und WeakSet. Ihre Schlüssel müssen Objekte sein, und sie halten diese Objekte nicht künstlich am Leben: Wird ein Schlüssel-Objekt sonst nirgends mehr referenziert, darf der Garbage Collector es entfernen – samt zugehörigem Eintrag. Das macht sie nützlich, um Zusatzdaten an Objekte zu hängen, ohne ein Speicherleck zu riskieren.

const metadaten = new WeakMap();

let element = { name: "Box" };
metadaten.set(element, { sichtbar: true });

console.log(metadaten.get(element)); // { sichtbar: true }

element = null; // Objekt wird irgendwann aufgeräumt – der Eintrag verschwindet mit

Der Preis dafür: WeakMaps und WeakSets sind nicht iterierbar und haben keine size. Für den Alltag greifst du also fast immer zur normalen Map oder Set.

Fazit

Objekte und Arrays bleiben deine Arbeitspferde, aber es lohnt sich, Map und Set im Werkzeugkasten zu haben. Nimm eine Map, wenn du beliebige Schlüsseltypen, eine verlässliche Reihenfolge oder häufiges Hinzufügen und Entfernen brauchst. Nimm ein Set, wenn Werte eindeutig sein müssen oder du oft prüfst, ob etwas schon enthalten ist. Sobald du den Reflex entwickelst, bei „eindeutig" an ein Set und bei „echtem Nachschlagewerk" an eine Map zu denken, wird dein Code nicht nur kürzer, sondern auch klarer in seiner Absicht.