std.ArrayHashMap — Mapa Hash com Ordem de Inserção
O std.ArrayHashMap é uma implementação de mapa hash que preserva a ordem de inserção dos elementos. Diferente do HashMap convencional, ele armazena os pares chave-valor em arrays contíguos, permitindo iteração na ordem em que foram inseridos e acesso por índice numérico. É ideal quando a ordem importa e também oferece excelente desempenho em cache.
Visão Geral
const std = @import("std");
const ArrayHashMap = std.ArrayHashMap;
Tipo e Assinatura
pub fn ArrayHashMap(
comptime K: type,
comptime V: type,
comptime Context: type,
comptime store_hash: bool,
) type
Para a maioria dos casos, use std.AutoArrayHashMap que configura automaticamente o contexto de hashing:
pub fn AutoArrayHashMap(comptime K: type, comptime V: type) type
Existe também a versão não gerenciada (Unmanaged) que não armazena o alocador internamente, útil em contextos onde o alocador já é passado explicitamente.
Funções Principais
Criação e Destruição
// Cria um mapa vazio
pub fn init(allocator: Allocator) AutoArrayHashMap(K, V)
// Libera toda a memória
pub fn deinit(self: *Self) void
Inserção e Acesso
// Insere ou atualiza um par chave-valor
pub fn put(self: *Self, key: K, value: V) Allocator.Error!void
// Busca valor pela chave
pub fn get(self: Self, key: K) ?V
// Busca o ponteiro para o valor (permite modificação in-place)
pub fn getPtr(self: *Self, key: K) ?*V
// Retorna o índice da chave no array interno
pub fn getIndex(self: Self, key: K) ?usize
// Verifica se a chave existe
pub fn contains(self: Self, key: K) bool
Remoção
// Remove preservando a ordem (O(n))
pub fn orderedRemove(self: *Self, key: K) ?KV
// Remove por troca com último (O(1), não preserva ordem)
pub fn swapRemove(self: *Self, key: K) ?KV
Iteração e Acesso por Índice
// Acessa chaves e valores como slices
pub fn keys(self: Self) []K
pub fn values(self: Self) []V
// Iterador
pub fn iterator(self: Self) Iterator
Exemplo 1: Uso Básico — Contagem de Palavras
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var contagem = std.AutoArrayHashMap([]const u8, u32).init(allocator);
defer contagem.deinit();
const palavras = [_][]const u8{
"zig", "é", "rápido", "zig", "é", "seguro", "zig",
};
for (palavras) |palavra| {
const entry = try contagem.getOrPut(palavra);
if (entry.found_existing) {
entry.value_ptr.* += 1;
} else {
entry.value_ptr.* = 1;
}
}
const stdout = std.io.getStdOut().writer();
// Iteração preserva a ordem de inserção
var it = contagem.iterator();
while (it.next()) |entry| {
try stdout.print("{s}: {d}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
// Saída:
// zig: 3
// é: 2
// rápido: 1
// seguro: 1
}
Exemplo 2: Registro Ordenado de Configurações
const std = @import("std");
const Config = struct {
valor: []const u8,
obrigatorio: bool,
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var config = std.AutoArrayHashMap([]const u8, Config).init(allocator);
defer config.deinit();
try config.put("host", .{ .valor = "localhost", .obrigatorio = true });
try config.put("porta", .{ .valor = "8080", .obrigatorio = true });
try config.put("debug", .{ .valor = "false", .obrigatorio = false });
try config.put("log_nivel", .{ .valor = "info", .obrigatorio = false });
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("=== Configuração ===\n");
// Acesso direto por slices (ordem preservada)
const chaves = config.keys();
const valores = config.values();
for (chaves, valores) |chave, valor| {
const tag = if (valor.obrigatorio) "[OBR]" else "[OPC]";
try stdout.print("{s} {s} = {s}\n", .{ tag, chave, valor.valor });
}
// Busca individual
if (config.get("porta")) |porta| {
try stdout.print("\nPorta configurada: {s}\n", .{porta.valor});
}
}
Exemplo 3: Acesso por Índice
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var mapa = std.AutoArrayHashMap([]const u8, i32).init(allocator);
defer mapa.deinit();
try mapa.put("alfa", 1);
try mapa.put("beta", 2);
try mapa.put("gama", 3);
try mapa.put("delta", 4);
const stdout = std.io.getStdOut().writer();
// Acesso por índice numérico
const chaves = mapa.keys();
const valores = mapa.values();
try stdout.print("Primeiro: {s} = {d}\n", .{ chaves[0], valores[0] });
try stdout.print("Último: {s} = {d}\n", .{
chaves[chaves.len - 1],
valores[valores.len - 1],
});
// Encontra o índice de uma chave
if (mapa.getIndex("gama")) |idx| {
try stdout.print("'gama' está no índice {d}\n", .{idx});
}
try stdout.print("Total de entradas: {d}\n", .{mapa.count()});
}
ArrayHashMap vs HashMap
| Característica | ArrayHashMap | HashMap |
|---|---|---|
| Ordem de inserção | Preservada | Não garantida |
| Acesso por índice | Sim | Não |
| Desempenho de lookup | Bom | Excelente |
| Uso de memória | Menor | Maior |
| Cache-friendliness | Melhor | Pior |
Use ArrayHashMap quando precisar de ordem de inserção ou acesso por índice. Use HashMap quando o desempenho puro de lookup for prioridade.
Módulos Relacionados
- std.HashMap — HashMap sem ordenação
- std.StringHashMap — HashMap especializado para chaves string
- std.ArrayList — Array dinâmico
- std.mem.Allocator — Interface de alocação