Hash map
Ultimo aggiornamento
Una hash map memorizza coppie chiave-valore e ti permette di trovare un valore tramite la sua chiave in O(1) in media. Funziona esattamente come una tabella hash, ma ogni voce di un bucket contiene sia una chiave sia il valore associato. Per salvare o recuperare una coppia, la chiave viene trasformata con l'hash in un indice di bucket, poi la catena del bucket viene scorsa alla ricerca della chiave corrispondente. Premi play qui sopra per vedere le coppie collocate nel bucket calcolato con l'hash e un valore recuperato tramite la sua chiave.
È la struttura dietro il dict di Python, la HashMap di Java e Map/gli oggetti di JavaScript. Le collisioni si gestiscono come in una tabella hash, qui con il concatenamento separato, quindi le prestazioni dipendono da una buona funzione di hash e da un fattore di carico basso.
Complessità temporale
| Operazione | Media | Caso peggiore |
|---|---|---|
| Put (inserimento/aggiornamento) | O(1) | O(n) |
| Get (ricerca) | O(1) | O(n) |
| Eliminazione | O(1) | O(n) |
| Spazio | O(n) | O(n) |
La hash map nei linguaggi più comuni
| Linguaggio | Tipo |
|---|---|
| Python | dict |
| Java | HashMap |
| JavaScript | Map / oggetto |
| C++ | std::unordered_map |
| Go | map |
Esempio svolto
Inserimento delle coppie ("cat", 3), ("dog", 5), ("cat", 9), ("emu", 7) in una mappa con 8 bucket, usando hash(key) % 8. Supponiamo che hash("cat") % 8 = 2, hash("dog") % 8 = 5, hash("emu") % 8 = 2:
| Passo | Struttura | Azione |
|---|---|---|
Put ("cat", 3) | bucket 2: [("cat", 3)] | L'hash porta al bucket 2; catena vuota, quindi aggiungi la coppia. |
Put ("dog", 5) | bucket 2: [("cat", 3)], bucket 5: [("dog", 5)] | L'hash porta al bucket 5; catena vuota, quindi aggiungi la coppia. |
Put ("cat", 9) | bucket 2: [("cat", 9)], bucket 5: [("dog", 5)] | L'hash porta al bucket 2; la chiave "cat" è già nella catena, quindi aggiorna il suo valore a 9. |
Put ("emu", 7) | bucket 2: [("cat", 9), ("emu", 7)], bucket 5: [("dog", 5)] | L'hash porta al bucket 2; collide con "cat", la chiave non c'è, quindi aggiungi la coppia. |
Get "emu" | bucket 2: [("cat", 9), ("emu", 7)] | L'hash porta al bucket 2; scorri la catena, salta "cat", trova "emu", restituisci 7. |
Quando usare una hash map
| Usala quando | Evitala quando |
|---|---|
Ti servono ricerca, inserimento ed eliminazione O(1) in media tramite una chiave esatta. | Ti servono chiavi in ordine o query su intervalli: usa un BST bilanciato o una mappa ordinata. |
| Le chiavi sono hashable e l'uguaglianza è ben definita (stringhe, interi, tuple). | Le chiavi sono mutabili e possono cambiare dopo l'inserimento, corrompendo il loro bucket. |
| Stai contando, eliminando duplicati, facendo caching o indicizzando per identificatore. | Ti servono garanzie O(log n) nel caso peggiore invece di limiti medi ammortizzati. |
| L'ordine di iterazione non conta, oppure basta una mappa che conserva l'ordine di inserimento. | La memoria è molto limitata: bucket, margine per il fattore di carico e puntatori aggiungono overhead. |
Codice Hash Map
Un'implementazione di Hash Map pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.
Codice Hash Map 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))Codice Hash Map 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(", "));Codice Hash Map 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}Codice Hash Map 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}Codice Hash Map 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}Domande frequenti sulla hash map
Qual è la differenza tra una hash map e una tabella hash?
Qual è la complessità temporale di una hash map?
O(1) in media con una buona funzione di hash e un fattore di carico basso. Nel caso patologico in cui tutte le chiavi collidono in un unico bucket, degradano a O(n), ed è per questo che il ridimensionamento e un buon hashing contano.Come gestisce una hash map due chiavi che finiscono nello stesso bucket?
Hash map o albero binario di ricerca: quale usare?
O(1) in media. Usa un albero binario di ricerca bilanciato (come una TreeMap o una std::map) quando ti servono chiavi in ordine, query su intervalli o ricerche di predecessore/successore, che costano O(log n). Il BST sacrifica un po' di velocità in cambio di garanzie di ordine che una hash map non può offrire.Cos'è il fattore di carico e perché fa scattare il ridimensionamento?
0.75. Il ridimensionamento è O(n) ma raro, quindi il suo costo si ammortizza e le operazioni restano O(1) in media.Posso usare un oggetto mutabile come una lista come chiave di una hash map?
TypeError: unhashable type per una list. Se modifichi una chiave dopo averla inserita, il suo hash cambia e la mappa non riesce più a trovarla nel bucket giusto, perdendo la voce senza avvisarti. Usa chiavi immutabili come stringhe, numeri o tuple.