# ArrayList, HashMap and Other Containers — Zig

Source: https://www.geekswithgeeks.com/en/zig/m-collections

> Use std containers with explicit allocators.

## Growable containers

The standard library provides containers that take allocators. **`std.ArrayList(T)`** is a growable array; since Zig 0.15 it is **unmanaged** by default: it does not store an allocator, so you pass one to methods that may allocate (`try list.append(gpa, item)`, `list.deinit(gpa)`), and you create it empty with `.empty`. Its `items` field is a slice of the current elements; **appending can reallocate**, so slices and pointers into `items` may become invalid. `toOwnedSlice(gpa)` transfers ownership of the buffer to the caller. **Hash maps** come in several flavours: `std.AutoHashMap(K, V)` hashes most key types automatically, `std.StringHashMap(V)` uses string contents as keys, and `ArrayHashMap` variants preserve insertion order; **`getOrPut`** inserts or updates in one lookup. Keys and values are stored by value, so **string keys are not copied**: the bytes must outlive the map, or you must `dupe` them. Other containers include `std.MultiArrayList` (struct-of-arrays layout for cache efficiency), `std.PriorityQueue`, `std.BufSet` and `std.SinglyLinkedList`. Because the standard library is evolving, check the API for your Zig version, especially whether a container is managed or unmanaged.

## Revenue by city with ArrayList and StringHashMap

Unmanaged ArrayList (Zig 0.15 style) and getOrPut.

```zig
const std = @import("std");

const Order = struct { city: []const u8, total_paise: u64 };

pub fn main() !void {
    var debug_alloc: std.heap.DebugAllocator(.{}) = .init;
    defer _ = debug_alloc.deinit();
    const gpa = debug_alloc.allocator();

    // ArrayList is unmanaged in Zig 0.15: pass the allocator to methods
    var orders: std.ArrayList(Order) = .empty;
    defer orders.deinit(gpa);
    try orders.append(gpa, .{ .city = "Pune", .total_paise = 120_000 });
    try orders.append(gpa, .{ .city = "Delhi", .total_paise = 30_000 });
    try orders.append(gpa, .{ .city = "Pune", .total_paise = 80_000 });

    // StringHashMap stores the allocator; keys must outlive the map (literals do)
    var revenue = std.StringHashMap(u64).init(gpa);
    defer revenue.deinit();

    for (orders.items) |o| {
        const entry = try revenue.getOrPut(o.city);
        if (!entry.found_existing) entry.value_ptr.* = 0;
        entry.value_ptr.* += o.total_paise;
    }

    var it = revenue.iterator();
    while (it.next()) |kv| {
        std.debug.print("{s}: {d}\n", .{ kv.key_ptr.*, kv.value_ptr.* });
    }

    const last = orders.pop();          // ?Order: null when empty
    if (last) |o| std.debug.print("removed last order from {s}\n", .{o.city});
}
```

## Beware of invalidated slices

Holding `const first = &list.items[0];` and then appending may move the buffer, leaving `first` dangling. Store indices instead of pointers when a container can grow.

**Quiz:** In Zig 0.15, how do you append to an unmanaged std.ArrayList?

- [x] try list.append(allocator, item)
- [ ] list.append(item)
- [ ] list.push(item)
- [ ] ArrayLists cannot grow

*Answer:* try list.append(allocator, item). Unmanaged containers take the allocator as an argument to allocating methods.
