Hash Map
Última actualización
Un hash map almacena pares clave-valor y permite buscar un valor por su clave en O(1) en promedio. Funciona igual que una tabla hash, pero cada entrada de cubeta lleva tanto una clave como su valor asociado. Para almacenar o recuperar un par, la clave se hashea a un índice de cubeta y luego se recorre la cadena de esa cubeta buscando la clave coincidente. Pulsa reproducir arriba para ver cómo los pares se colocan por cubeta hasheada y cómo se recupera un valor por su clave.
Esta es la estructura detrás del dict de Python, del HashMap de Java y del Map/objetos de JavaScript. Las colisiones se manejan igual que en una tabla hash - aquí, mediante encadenamiento separado - así que el rendimiento depende de una buena función de hash y de un factor de carga bajo.
Complejidad temporal
| Operación | Promedio | Peor caso |
|---|---|---|
| Insertar/actualizar (put) | O(1) | O(n) |
| Buscar (get) | O(1) | O(n) |
| Eliminar | O(1) | O(n) |
| Espacio | O(n) | O(n) |
Hash map en lenguajes comunes
| Lenguaje | Tipo |
|---|---|
| Python | dict |
| Java | HashMap |
| JavaScript | Map / objeto |
| C++ | std::unordered_map |
| Go | map |
Ejemplo resuelto
Insertando los pares ("cat", 3), ("dog", 5), ("cat", 9), ("emu", 7) en un mapa con 8 cubetas, usando hash(key) % 8. Supongamos hash("cat") % 8 = 2, hash("dog") % 8 = 5, hash("emu") % 8 = 2:
| Paso | Estructura | Acción |
|---|---|---|
Put ("cat", 3) | cubeta 2: [("cat", 3)] | Se hashea a la cubeta 2; cadena vacía, así que se añade el par. |
Put ("dog", 5) | cubeta 2: [("cat", 3)], cubeta 5: [("dog", 5)] | Se hashea a la cubeta 5; cadena vacía, así que se añade el par. |
Put ("cat", 9) | cubeta 2: [("cat", 9)], cubeta 5: [("dog", 5)] | Se hashea a la cubeta 2; la clave "cat" ya está en la cadena, así que se actualiza su valor a 9. |
Put ("emu", 7) | cubeta 2: [("cat", 9), ("emu", 7)], cubeta 5: [("dog", 5)] | Se hashea a la cubeta 2; colisiona con "cat", la clave no se encuentra, así que se añade el par. |
Get "emu" | cubeta 2: [("cat", 9), ("emu", 7)] | Se hashea a la cubeta 2; se recorre la cadena, se salta "cat", coincide "emu", se devuelve 7. |
Cuándo usar un hash map
| Úsalo cuando | Evítalo cuando |
|---|---|
Necesitas búsqueda, inserción y eliminación en O(1) promedio por una clave exacta. | Necesitas mantener las claves ordenadas o hacer consultas por rango - usa un BST balanceado o un mapa ordenado. |
| Las claves son hasheables y su igualdad está bien definida (cadenas, enteros, tuplas). | Las claves son mutables y pueden cambiar tras la inserción, lo que corrompe su cubeta. |
| Estás contando, deduplicando, cacheando o indexando por un identificador. | Necesitas garantías de peor caso O(log n) en lugar de cotas promedio amortizadas. |
| El orden de iteración no importa, o basta con un mapa ordenado por inserción. | La memoria es muy escasa - las cubetas, el margen del factor de carga y los punteros añaden sobrecarga. |
Código de Hash Map
Una implementación limpia y ejecutable de Hash Map en Python, JavaScript, Java, C++, C. Elige un lenguaje, copia el código o ábrelo ya cargado en el Playground de Coddy.
Código de Hash Map en 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))Código de Hash Map en 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(", "));Código de Hash Map en 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}Código de Hash Map en 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}Código de Hash Map en 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}Preguntas frecuentes sobre hash map
¿Cuál es la diferencia entre un hash map y una tabla hash?
¿Cuál es la complejidad temporal de un hash map?
O(1) en promedio con una buena función de hash y un factor de carga bajo. En el caso patológico en que todas las claves colisionan en una sola cubeta, degeneran a O(n), por lo que el redimensionamiento y un buen hashing importan.¿Cómo maneja un hash map dos claves que hashean a la misma cubeta?
Hash map vs árbol binario de búsqueda: ¿cuál debería usar?
O(1) promedio. Usa un árbol binario de búsqueda balanceado (como TreeMap o std::map) cuando necesitas claves ordenadas, consultas por rango o búsquedas de predecesor/sucesor, que cuestan O(log n). El BST cambia algo de velocidad por garantías de orden que un hash map no puede ofrecer.¿Qué es el factor de carga y por qué desencadena el redimensionamiento?
0.75. El redimensionamiento es O(n) pero poco frecuente, por lo que se amortiza y mantiene las operaciones promedio en O(1).¿Puedo usar un objeto mutable como una lista como clave de un hash map?
TypeError: unhashable type para una list, por ejemplo. Si mutas una clave después de insertarla, su hash cambia y el mapa ya no puede encontrarla en la cubeta correcta, perdiendo silenciosamente la entrada. Usa claves inmutables como cadenas, números o tuplas.