Hash Map
Última atualização
Um hash map armazena pares chave-valor e permite buscar um valor pela sua chave em O(1) em média. Ele funciona exatamente como uma tabela hash, mas cada entrada de balde carrega tanto uma chave quanto seu valor associado. Para armazenar ou recuperar um par, a chave é hasheada em um índice de balde e, em seguida, a cadeia daquele balde é percorrida em busca da chave correspondente. Pressione reproduzir acima para ver os pares serem colocados por balde hasheado e um valor ser recuperado pela sua chave.
Esta é a estrutura por trás do dict do Python, do HashMap do Java e do Map/objetos do JavaScript. As colisões são tratadas do mesmo jeito que em uma tabela hash - aqui, por encadeamento separado - então o desempenho depende de uma boa função de hash e de um fator de carga baixo.
Complexidade de tempo
| Operação | Média | Pior caso |
|---|---|---|
| Inserir/atualizar (put) | O(1) | O(n) |
| Buscar (get) | O(1) | O(n) |
| Remover | O(1) | O(n) |
| Espaço | O(n) | O(n) |
Hash map em linguagens comuns
| Linguagem | Tipo |
|---|---|
| Python | dict |
| Java | HashMap |
| JavaScript | Map / objeto |
| C++ | std::unordered_map |
| Go | map |
Exemplo resolvido
Inserindo os pares ("cat", 3), ("dog", 5), ("cat", 9), ("emu", 7) em um mapa com 8 baldes, usando hash(key) % 8. Suponha hash("cat") % 8 = 2, hash("dog") % 8 = 5, hash("emu") % 8 = 2:
| Passo | Estrutura | Ação |
|---|---|---|
Put ("cat", 3) | balde 2: [("cat", 3)] | Hasheia para o balde 2; cadeia vazia, então adiciona o par. |
Put ("dog", 5) | balde 2: [("cat", 3)], balde 5: [("dog", 5)] | Hasheia para o balde 5; cadeia vazia, então adiciona o par. |
Put ("cat", 9) | balde 2: [("cat", 9)], balde 5: [("dog", 5)] | Hasheia para o balde 2; a chave "cat" já está na cadeia, então atualiza seu valor para 9. |
Put ("emu", 7) | balde 2: [("cat", 9), ("emu", 7)], balde 5: [("dog", 5)] | Hasheia para o balde 2; colide com "cat", a chave não é encontrada, então adiciona o par. |
Get "emu" | balde 2: [("cat", 9), ("emu", 7)] | Hasheia para o balde 2; percorre a cadeia, pula "cat", casa com "emu", retorna 7. |
Quando usar um hash map
| Use quando | Evite quando |
|---|---|
Você precisa de busca, inserção e remoção em O(1) médio por uma chave exata. | Você precisa manter as chaves ordenadas ou fazer consultas por intervalo - use uma BST balanceada ou um mapa ordenado. |
| As chaves são hasheáveis e a igualdade é bem definida (strings, inteiros, tuplas). | As chaves são mutáveis e podem mudar após a inserção, o que corrompe seu balde. |
| Você está contando, deduplicando, fazendo cache ou indexando por um identificador. | Você precisa de garantias de pior caso O(log n) em vez de limites médios amortizados. |
| A ordem de iteração não importa, ou um mapa ordenado por inserção é suficiente. | A memória é muito limitada - baldes, folga do fator de carga e ponteiros acrescentam sobrecarga. |
Código de Hash Map
Uma implementação limpa e executável de Hash Map em Python, JavaScript, Java, C++, C. Escolha uma linguagem, copie o código ou abra-o já carregado no Playground da Coddy.
Código de Hash Map em 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 em 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 em 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 em 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 em 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}Perguntas frequentes sobre hash map
Qual é a diferença entre um hash map e uma tabela hash?
Qual é a complexidade de tempo de um hash map?
O(1) em média com uma boa função de hash e um fator de carga baixo. No caso patológico em que todas as chaves colidem em um único balde, elas degeneram para O(n), por isso o redimensionamento e um bom hashing importam.Como um hash map lida com duas chaves que hasheiam para o mesmo balde?
Hash map vs árvore binária de busca: qual devo usar?
O(1) médio. Use uma árvore binária de busca balanceada (como TreeMap ou std::map) quando precisar de chaves ordenadas, consultas por intervalo ou buscas de predecessor/sucessor, que custam O(log n). A BST troca um pouco de velocidade por garantias de ordenação que um hash map não consegue oferecer.O que é o fator de carga e por que ele dispara o redimensionamento?
0.75. O redimensionamento é O(n) mas raro, então é amortizado e mantém as operações médias em O(1).Posso usar um objeto mutável como uma lista como chave de um hash map?
TypeError: unhashable type para uma list, por exemplo. Se você mutar uma chave após inseri-la, seu hash muda e o mapa não consegue mais encontrá-la no balde certo, perdendo a entrada silenciosamente. Use chaves imutáveis como strings, números ou tuplas.