Introdução
Um HashMap (também chamado de dicionário ou mapa) é uma estrutura de dados que associa chaves a valores, permitindo inserção, busca e remoção em tempo O(1) amortizado. Em Zig, a biblioteca padrão oferece std.HashMap, std.AutoHashMap e std.StringHashMap para diferentes tipos de chave.
Nesta receita, você aprenderá a usar essas estruturas para resolver problemas comuns do dia a dia.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
- Familiaridade com alocadores
HashMap Básico com AutoHashMap
AutoHashMap detecta automaticamente a função de hash para o tipo da chave:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar HashMap de i32 -> []const u8
var mapa = std.AutoHashMap(i32, []const u8).init(allocator);
defer mapa.deinit();
// Inserir pares chave-valor
try mapa.put(1, "um");
try mapa.put(2, "dois");
try mapa.put(3, "três");
try mapa.put(4, "quatro");
// Buscar um valor
if (mapa.get(2)) |valor| {
std.debug.print("Chave 2: {s}\n", .{valor});
}
// Verificar se chave existe
std.debug.print("Contém 3? {}\n", .{mapa.contains(3)});
std.debug.print("Contém 5? {}\n", .{mapa.contains(5)});
// Tamanho
std.debug.print("Total de entradas: {d}\n", .{mapa.count()});
}
Saída esperada
Chave 2: dois
Contém 3? true
Contém 5? false
Total de entradas: 4
StringHashMap para Chaves de Texto
StringHashMap é otimizado para chaves do tipo []const u8:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var capitais = std.StringHashMap([]const u8).init(allocator);
defer capitais.deinit();
try capitais.put("Brasil", "Brasília");
try capitais.put("Argentina", "Buenos Aires");
try capitais.put("Chile", "Santiago");
try capitais.put("Peru", "Lima");
try capitais.put("Colômbia", "Bogotá");
// Buscar
const paises = [_][]const u8{ "Brasil", "Chile", "México" };
for (&paises) |pais| {
if (capitais.get(pais)) |capital| {
std.debug.print("{s} -> {s}\n", .{ pais, capital });
} else {
std.debug.print("{s} -> não encontrado\n", .{pais});
}
}
}
Iterar sobre um HashMap
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var estoque = std.StringHashMap(u32).init(allocator);
defer estoque.deinit();
try estoque.put("Teclado", 45);
try estoque.put("Mouse", 120);
try estoque.put("Monitor", 15);
try estoque.put("Headset", 30);
// Iterar sobre todas as entradas
std.debug.print("=== Estoque ===\n", .{});
var it = estoque.iterator();
while (it.next()) |entry| {
std.debug.print(" {s}: {d} unidades\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
// Iterar apenas sobre as chaves
std.debug.print("\nProdutos: ", .{});
var kit = estoque.keyIterator();
while (kit.next()) |key_ptr| {
std.debug.print("{s} ", .{key_ptr.*});
}
std.debug.print("\n", .{});
}
Atualizar e Remover Entradas
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var pontuacao = std.StringHashMap(u32).init(allocator);
defer pontuacao.deinit();
try pontuacao.put("Alice", 100);
try pontuacao.put("Bob", 85);
try pontuacao.put("Carlos", 90);
// Atualizar valor existente (put sobrescreve)
try pontuacao.put("Bob", 95);
// getOrPut: obtém existente ou insere novo
const result = try pontuacao.getOrPut("Diana");
if (!result.found_existing) {
result.value_ptr.* = 0; // Valor padrão para nova entrada
}
std.debug.print("Diana: {d}\n", .{result.value_ptr.*});
// Remover uma entrada
const removido = pontuacao.fetchRemove("Carlos");
if (removido) |kv| {
std.debug.print("Removido Carlos com pontuação {d}\n", .{kv.value});
}
// Listar resultado final
std.debug.print("\nPontuação final:\n", .{});
var it = pontuacao.iterator();
while (it.next()) |entry| {
std.debug.print(" {s}: {d}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
}
Exemplo Prático: Contar Frequência de Palavras
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const texto = "zig é rápido zig é seguro zig é incrível zig zig zig";
var frequencia = std.StringHashMap(u32).init(allocator);
defer frequencia.deinit();
// Contar palavras
var palavras = std.mem.tokenizeAny(u8, texto, " ");
while (palavras.next()) |palavra| {
const result = try frequencia.getOrPut(palavra);
if (result.found_existing) {
result.value_ptr.* += 1;
} else {
result.value_ptr.* = 1;
}
}
// Exibir frequência
std.debug.print("Frequência de palavras:\n", .{});
var it = frequencia.iterator();
while (it.next()) |entry| {
std.debug.print(" \"{s}\": {d}x\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
std.debug.print("Palavras distintas: {d}\n", .{frequencia.count()});
}
HashMap com Valores Complexos
const std = @import("std");
const Produto = struct {
nome: []const u8,
preco: f64,
estoque: u32,
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var catalogo = std.StringHashMap(Produto).init(allocator);
defer catalogo.deinit();
try catalogo.put("SKU001", .{ .nome = "Teclado Mecânico", .preco = 299.90, .estoque = 45 });
try catalogo.put("SKU002", .{ .nome = "Mouse Gamer", .preco = 159.90, .estoque = 120 });
try catalogo.put("SKU003", .{ .nome = "Monitor 4K", .preco = 2499.00, .estoque = 8 });
// Buscar produto
if (catalogo.get("SKU002")) |produto| {
std.debug.print("Produto: {s}\n", .{produto.nome});
std.debug.print("Preço: R$ {d:.2}\n", .{produto.preco});
std.debug.print("Estoque: {d}\n", .{produto.estoque});
}
// Calcular valor total do estoque
var valor_total: f64 = 0;
var it = catalogo.iterator();
while (it.next()) |entry| {
const p = entry.value_ptr.*;
valor_total += p.preco * @as(f64, @floatFromInt(p.estoque));
}
std.debug.print("\nValor total em estoque: R$ {d:.2}\n", .{valor_total});
}
Dicas e Boas Práticas
Escolha o tipo certo: Use
StringHashMappara chaves de texto,AutoHashMappara tipos primitivos.Use
getOrPut: Para padrões de “inserir se não existe”,getOrPuté mais eficiente que verificar e inserir separadamente.Cuidado com referências: As chaves e valores são copiados no HashMap. Strings (
[]const u8) são ponteiros, então garanta que os dados originais permaneçam válidos.Pré-aloque com
ensureTotalCapacity: Se você sabe quantas entradas terá, pré-alocar evita realocações durante a inserção.Sempre libere: Use
defer mapa.deinit()logo após a criação.
Receitas Relacionadas
- Arrays Dinâmicos com ArrayList - Listas dinâmicas
- Conjunto (Set) de Valores Únicos - Sets baseados em HashMap
- Usando GeneralPurposeAllocator - Alocador de memória
- Como parsear JSON em Zig - JSON para HashMap