Hash Map
Zuletzt aktualisiert
Eine Hash Map speichert Schlüssel-Wert-Paare und erlaubt es, einen Wert per Schlüssel im Durchschnitt in O(1) nachzuschlagen. Sie funktioniert genau wie eine Hashtabelle, aber jeder Bucket-Eintrag trägt sowohl einen Schlüssel als auch seinen zugehörigen Wert. Um ein Paar zu speichern oder abzurufen, wird der Schlüssel auf einen Bucket-Index gehasht, dann wird die Kette dieses Buckets nach dem passenden Schlüssel durchsucht. Drücke oben auf Abspielen, um zu sehen, wie Paare nach gehashtem Bucket platziert und ein Wert per Schlüssel abgerufen wird.
Das ist die Struktur hinter Pythons dict, Javas HashMap und JavaScripts Map/Objekten. Kollisionen werden genauso behandelt wie in einer Hashtabelle - hier durch separate Verkettung - daher hängt die Leistung von einer guten Hash-Funktion und einem niedrigen Füllfaktor ab.
Zeitkomplexität
| Operation | Durchschnitt | Schlechtester Fall |
|---|---|---|
| Einfügen/aktualisieren (put) | O(1) | O(n) |
| Nachschlagen (get) | O(1) | O(n) |
| Löschen | O(1) | O(n) |
| Speicher | O(n) | O(n) |
Hash Map in gängigen Sprachen
| Sprache | Typ |
|---|---|
| Python | dict |
| Java | HashMap |
| JavaScript | Map / Objekt |
| C++ | std::unordered_map |
| Go | map |
Durchgerechnetes Beispiel
Einfügen der Paare ("cat", 3), ("dog", 5), ("cat", 9), ("emu", 7) in eine Map mit 8 Buckets, mit hash(key) % 8. Angenommen hash("cat") % 8 = 2, hash("dog") % 8 = 5, hash("emu") % 8 = 2:
| Schritt | Struktur | Aktion |
|---|---|---|
Put ("cat", 3) | Bucket 2: [("cat", 3)] | Hasht auf Bucket 2; Kette leer, also wird das Paar angehängt. |
Put ("dog", 5) | Bucket 2: [("cat", 3)], Bucket 5: [("dog", 5)] | Hasht auf Bucket 5; Kette leer, also wird das Paar angehängt. |
Put ("cat", 9) | Bucket 2: [("cat", 9)], Bucket 5: [("dog", 5)] | Hasht auf Bucket 2; Schlüssel "cat" ist bereits in der Kette, also wird sein Wert auf 9 aktualisiert. |
Put ("emu", 7) | Bucket 2: [("cat", 9), ("emu", 7)], Bucket 5: [("dog", 5)] | Hasht auf Bucket 2; kollidiert mit "cat", Schlüssel nicht gefunden, also wird das Paar angehängt. |
Get "emu" | Bucket 2: [("cat", 9), ("emu", 7)] | Hasht auf Bucket 2; durchsucht die Kette, überspringt "cat", trifft "emu", gibt 7 zurück. |
Wann eine Hash Map verwenden
| Verwenden, wenn | Vermeiden, wenn |
|---|---|
Du im Durchschnitt O(1)-Nachschlagen, -Einfügen und -Löschen per exaktem Schlüssel brauchst. | Du Schlüssel sortiert halten oder Bereichsabfragen brauchst - nutze einen balancierten BST oder eine sortierte Map. |
| Schlüssel hashbar sind und Gleichheit wohldefiniert ist (Strings, Ints, Tupel). | Schlüssel veränderlich sind und sich nach dem Einfügen ändern können, was ihren Bucket verfälscht. |
| Du nach einem Bezeichner zählst, dedupliziert, cachst oder indexierst. | Du Worst-Case-O(log n)-Garantien statt amortisierter Durchschnittsschranken brauchst. |
| Die Iterationsreihenfolge egal ist oder eine einfügereihenfolge-Map genügt. | Der Speicher sehr knapp ist - Buckets, Füllfaktor-Spielraum und Zeiger verursachen Overhead. |
Hash Map-Code
Eine saubere, lauffähige Hash Map-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.
Hash Map-Code in Python
1# Python's built-in dict is a hash map: O(1) average2# insert, lookup, and delete.3inventory = {"apple": 3, "banana": 7}4
5# Insert and update6inventory["cherry"] = 57inventory["apple"] += 28
9# Lookup, with .get for a safe default on missing keys10print("apple: ", inventory["apple"])11print("mango: ", inventory.get("mango", 0))12
13# Membership test and delete14print("banana in stock:", "banana" in inventory)15del inventory["banana"]16print("banana in stock:", "banana" in inventory)17
18# Iterate over key-value pairs19for fruit, count in sorted(inventory.items()):20 print(f"{fruit}: {count}")21
22# Classic hash map use case: counting frequencies23words = "the quick brown fox jumps over the lazy dog the end".split()24freq = {}25for word in words:26 freq[word] = freq.get(word, 0) + 127
28print("Occurrences of the:", freq["the"])29print("Most common word: ", max(freq, key=freq.get))Hash Map-Code in JavaScript
1// The built-in Map is the idiomatic hash map in JavaScript:2// any key type, insertion-ordered iteration, O(1) average lookups.3const ages = new Map();4
5ages.set("Alice", 30);6ages.set("Bob", 25);7ages.set("Carol", 35);8ages.set("Bob", 26); // set on an existing key updates the value9
10console.log("Bob:", ages.get("Bob"));11console.log("Has Dave?", ages.has("Dave"));12console.log("Size:", ages.size);13
14ages.delete("Carol");15console.log("Size after delete:", ages.size);16
17for (const [name, age] of ages) {18 console.log(`${name} is ${age}`);19}20
21console.log("Keys:", [...ages.keys()].join(", "));Hash Map-Code in Java
1import java.util.HashMap;2import java.util.Map;3
4public class Main {5 public static void main(String[] args) {6 Map<String, Integer> ages = new HashMap<>();7 ages.put("Alice", 30);8 ages.put("Bob", 25);9 ages.put("Carol", 35);10
11 System.out.println("Bob -> " + ages.get("Bob"));12 System.out.println("contains Carol: " + ages.containsKey("Carol"));13 System.out.println("Dave or default: " + ages.getOrDefault("Dave", -1));14
15 // getOrDefault makes frequency counting a one-liner16 String[] words = {"apple", "banana", "apple", "cherry", "apple"};17 Map<String, Integer> counts = new HashMap<>();18 for (String w : words) {19 counts.put(w, counts.getOrDefault(w, 0) + 1);20 }21 System.out.println("apple count: " + counts.get("apple"));22
23 ages.remove("Bob");24 System.out.println("size after remove: " + ages.size());25
26 for (Map.Entry<String, Integer> entry : ages.entrySet()) {27 System.out.println(entry.getKey() + " is " + entry.getValue());28 }29 }30}Hash Map-Code in C++
1#include <algorithm>2#include <iostream>3#include <string>4#include <unordered_map>5#include <vector>6
7int main() {8 std::unordered_map<std::string, int> ages;9
10 // Insert and update11 ages["alice"] = 30; // operator[] inserts or overwrites12 ages.insert({"bob", 25}); // insert keeps an existing value13 ages.emplace("carol", 41);14 ages["alice"] = 31;15
16 // Lookup17 auto it = ages.find("bob");18 if (it != ages.end()) {19 std::cout << "bob is " << it->second << "\n";20 }21 std::cout << "has dave: " << ages.count("dave") << "\n";22
23 // Remove24 ages.erase("carol");25
26 // Iterate (sort keys first: unordered_map has no defined order)27 std::vector<std::string> keys;28 for (const auto& entry : ages) keys.push_back(entry.first);29 std::sort(keys.begin(), keys.end());30 for (const std::string& name : keys) {31 std::cout << name << " = " << ages[name] << "\n";32 }33 std::cout << "size: " << ages.size() << "\n";34 return 0;35}Hash Map-Code in C
1#include <stdbool.h>2#include <stdio.h>3#include <stdlib.h>4#include <string.h>5
6#define BUCKETS 167
8typedef struct Node {9 char key[24];10 int value;11 struct Node* next;12} Node;13
14// A small string -> int hash map with a reusable put/get/remove API15typedef struct {16 Node* buckets[BUCKETS];17 int size;18} HashMap;19
20unsigned int hashKey(const char* key) {21 unsigned int h = 5381;22 for (; *key; key++) h = h * 33 + (unsigned char)*key;23 return h % BUCKETS;24}25
26void mapPut(HashMap* map, const char* key, int value) {27 unsigned int i = hashKey(key);28 for (Node* n = map->buckets[i]; n != NULL; n = n->next) {29 if (strcmp(n->key, key) == 0) {30 n->value = value;31 return;32 }33 }34 Node* n = malloc(sizeof(Node));35 strcpy(n->key, key);36 n->value = value;37 n->next = map->buckets[i];38 map->buckets[i] = n;39 map->size++;40}41
42bool mapGet(const HashMap* map, const char* key, int* out) {43 for (Node* n = map->buckets[hashKey(key)]; n != NULL; n = n->next) {44 if (strcmp(n->key, key) == 0) {45 *out = n->value;46 return true;47 }48 }49 return false;50}51
52void mapRemove(HashMap* map, const char* key) {53 Node** link = &map->buckets[hashKey(key)];54 while (*link != NULL) {55 if (strcmp((*link)->key, key) == 0) {56 Node* old = *link;57 *link = old->next;58 free(old);59 map->size--;60 return;61 }62 link = &(*link)->next;63 }64}65
66int main(void) {67 HashMap map = {0};68 mapPut(&map, "alice", 30);69 mapPut(&map, "bob", 25);70 mapPut(&map, "carol", 41);71 mapPut(&map, "alice", 31); // updates the existing key72 int age;73 if (mapGet(&map, "bob", &age)) printf("bob is %d\n", age);74 printf("has dave: %s\n", mapGet(&map, "dave", &age) ? "yes" : "no");75 mapRemove(&map, "carol");76 printf("size: %d\n", map.size);77 return 0;78}Hash-Map-FAQ
Was ist der Unterschied zwischen einer Hash Map und einer Hashtabelle?
Wie ist die Zeitkomplexität einer Hash Map?
O(1) mit einer guten Hash-Funktion und einem niedrigen Füllfaktor. Im pathologischen Fall, in dem alle Schlüssel in einen einzigen Bucket kollidieren, degenerieren sie zu O(n), weshalb Vergrößern und gutes Hashing wichtig sind.Wie behandelt eine Hash Map zwei Schlüssel, die auf denselben Bucket hashen?
Hash Map vs. binärer Suchbaum: Was sollte ich verwenden?
O(1)-Durchschnittsoperationen willst. Verwende einen balancierten binären Suchbaum (wie TreeMap oder std::map), wenn du sortierte Schlüssel, Bereichsabfragen oder Vorgänger-/Nachfolgersuchen brauchst, die O(log n) kosten. Der BST tauscht etwas Geschwindigkeit gegen Ordnungsgarantien, die eine Hash Map nicht bieten kann.Was ist der Füllfaktor und warum löst er ein Vergrößern aus?
0.75 überschreitet. Das Vergrößern ist O(n), aber selten, daher wird es amortisiert und hält Durchschnittsoperationen bei O(1).Kann ich ein veränderliches Objekt wie eine Liste als Hash-Map-Schlüssel verwenden?
TypeError: unhashable type für eine list. Wenn du einen Schlüssel nach dem Einfügen veränderst, ändert sich sein Hash und die Map findet ihn nicht mehr im richtigen Bucket und verliert den Eintrag stillschweigend. Verwende unveränderliche Schlüssel wie Strings, Zahlen oder Tupel.