⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Hash Maps

const std = @import("std");
const expect = std.testing.expect;

test "string keys" {
    const gpa = std.testing.allocator;
    var map: std.StringHashMapUnmanaged(u32) = .empty;
    defer map.deinit(gpa);

    try map.put(gpa, "one", 1);
    try map.put(gpa, "two", 2);

    try expect(map.get("one").? == 1);
    try expect(map.get("three") == null);
    try expect(map.count() == 2);
}

test "auto hash for simple keys" {
    const gpa = std.testing.allocator;
    // AutoHashMap derives hashing and equality from the key type.
    var map: std.AutoHashMapUnmanaged(u32, []const u8) = .empty;
    defer map.deinit(gpa);

    try map.put(gpa, 1, "one");
    try expect(std.mem.eql(u8, map.get(1).?, "one"));
}

test "getOrPut avoids hashing twice" {
    const gpa = std.testing.allocator;
    var counts: std.StringHashMapUnmanaged(u32) = .empty;
    defer counts.deinit(gpa);

    for ([_][]const u8{ "a", "b", "a" }) |word| {
        // One lookup instead of a get followed by a put.
        const entry = try counts.getOrPut(gpa, word);
        if (!entry.found_existing) entry.value_ptr.* = 0;
        entry.value_ptr.* += 1;
    }

    try expect(counts.get("a").? == 2);
    try expect(counts.get("b").? == 1);
}

test "iterate entries" {
    const gpa = std.testing.allocator;
    var map: std.AutoHashMapUnmanaged(u32, u32) = .empty;
    defer map.deinit(gpa);

    try map.put(gpa, 1, 10);
    try map.put(gpa, 2, 20);

    var total: u32 = 0;
    var it = map.iterator();
    while (it.next()) |entry| total += entry.value_ptr.*;
    try expect(total == 30);
}

test "remove" {
    const gpa = std.testing.allocator;
    var map: std.StringHashMapUnmanaged(u32) = .empty;
    defer map.deinit(gpa);

    try map.put(gpa, "gone", 1);
    try expect(map.remove("gone"));
    try expect(map.count() == 0);
}

Which map to pick

TypeFor
AutoHashMapUnmanaged(K, V)keys with derivable hashing (integers, enums, simple structs)
StringHashMapUnmanaged(V)[]const u8 keys
ArrayHashMapUnmanaged(K, V)when you need stable insertion order

Like ArrayList, these are unmanaged: .empty to create, allocator passed to each method that allocates.

AutoHashMap will refuse keys it cannot hash safely: slices, for instance, because hashing the pointer is almost never what you meant. That is why string keys get their own type.

getOrPut instead of get-then-put

The counting idiom looks like this:

const entry = try counts.getOrPut(gpa, word);
if (!entry.found_existing) entry.value_ptr.* = 0;
entry.value_ptr.* += 1;

One hash and one lookup, rather than two. entry.value_ptr points into the map, so writing through it updates the stored value in place.

Note that pointer is only valid until the next insertion: the same invalidation rule as ArrayList.items.

Keys are not copied

The map stores your key as given. For string keys that means the map holds a slice pointing at memory it does not own; if that memory is freed or reused, the map is corrupt. Either keep the keys alive yourself, or duplicate them on insert and free them on removal.