std.ArrayHashMap em Zig — Referência e Exemplos

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ísticaArrayHashMapHashMap
Ordem de inserçãoPreservadaNão garantida
Acesso por índiceSimNão
Desempenho de lookupBomExcelente
Uso de memóriaMenorMaior
Cache-friendlinessMelhorPior

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

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.