# Importing ArrayList

### ArrayList

An `ArrayList` is one of the most important data structures in Zig.

It is a growable array.

A normal Zig array has a fixed size:

```zig
const numbers = [_]i32{ 1, 2, 3 };
```

This array always contains exactly 3 items. You cannot add a fourth item later.

But many real programs need collections that grow and shrink dynamically:

- reading lines from a file
- storing network packets
- collecting search results
- building strings
- parsing JSON
- tracking game objects

That is where `ArrayList` becomes useful.

An `ArrayList` automatically manages a dynamic buffer in memory. When the list becomes full, it allocates a larger buffer and moves the data.

You can think of it like this:

```text
Fixed Array:
[1][2][3]

ArrayList:
capacity = 8
length   = 3

[1][2][3][ ][ ][ ][ ][ ]
```

The list currently stores 3 items, but it already reserved space for 8.

This allows fast appends.

## Importing ArrayList

`ArrayList` lives inside Zig’s standard library.

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

The actual type is:

```zig
std.ArrayList(T)
```

`T` is the element type.

Example:

```zig
std.ArrayList(i32)
```

This means:

> “An ArrayList storing `i32` values.”

## Creating an ArrayList

Here is the smallest working example:

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();
}
```

Read this slowly.

## Step 1: Create an Allocator

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
```

An `ArrayList` uses heap memory internally.

That means it needs an allocator.

The general purpose allocator is Zig’s standard heap allocator for normal programs.

## Step 2: Get the Allocator Interface

```zig
const allocator = gpa.allocator();
```

This gives us a `std.mem.Allocator` value.

The allocator is passed into containers like `ArrayList`.

## Step 3: Create the List

```zig
var list = std.ArrayList(i32).init(allocator);
```

This creates an empty list of `i32`.

At this point:

```text
length   = 0
capacity = 0
```

No elements exist yet.

## Step 4: Free the Memory

```zig
defer list.deinit();
```

`ArrayList` may allocate heap memory.

You must release that memory later.

`deinit()` frees the internal buffer.

This is extremely important in Zig. Memory cleanup is explicit.

## Appending Elements

Now let’s add items.

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();

    try list.append(10);
    try list.append(20);
    try list.append(30);

    std.debug.print("{any}\n", .{list.items});
}
```

Output:

```text
{ 10, 20, 30 }
```

## Why `append` Uses `try`

Notice this:

```zig
try list.append(10);
```

Appending may require allocating more memory.

Memory allocation can fail.

So `append` returns an error union.

That is why we use `try`.

This is one of Zig’s major ideas:

> operations that can fail must say so explicitly

## Understanding `items`

The actual elements live here:

```zig
list.items
```

`items` is a slice.

Type:

```zig
[]i32
```

You can use it like a normal slice.

Example:

```zig
std.debug.print("{}\n", .{list.items[0]});
```

Output:

```text
10
```

You can loop over it:

```zig
for (list.items) |value| {
    std.debug.print("{}\n", .{value});
}
```

## Length vs Capacity

An `ArrayList` tracks two important numbers.

### Length

How many elements currently exist.

```text
[1][2][3]
 ^
 length = 3
```

### Capacity

How many elements fit before reallocating.

```text
[1][2][3][ ][ ][ ][ ][ ]
 ^
 capacity = 8
```

You can inspect them:

```zig
std.debug.print("len = {}\n", .{list.items.len});
std.debug.print("capacity = {}\n", .{list.capacity});
```

## Automatic Growth

When the list becomes full, Zig allocates a larger buffer.

Example:

```text
capacity = 4

[1][2][3][4]
```

Appending one more item might create:

```text
capacity = 8

[1][2][3][4][5][ ][ ][ ]
```

The old data gets copied into the new buffer.

This is why appending is usually fast, but occasionally more expensive.

## Removing Elements

### pop()

Removes the last item.

```zig
const value = list.pop();
```

Example:

```zig
try list.append(10);
try list.append(20);

const x = list.pop();

std.debug.print("{}\n", .{x});
```

Output:

```text
20
```

The list now contains only:

```text
10
```

## Inserting Elements

You can insert into the middle.

```zig
try list.insert(1, 99);
```

Example:

```text
Before:
[10][20][30]

After:
[10][99][20][30]
```

Elements shift to the right.

## Removing by Index

```zig
_ = list.orderedRemove(1);
```

Example:

```text
Before:
[10][20][30]

After:
[10][30]
```

This preserves order.

But shifting elements costs time.

## Fast Unordered Removal

If order does not matter:

```zig
_ = list.swapRemove(1);
```

Example:

```text
Before:
[10][20][30][40]
```

Remove index 1:

```text
After:
[10][40][30]
```

The last element moves into the removed slot.

This is much faster.

## Reserving Capacity

Sometimes you already know the approximate size.

Example:

```zig
try list.ensureTotalCapacity(1000);
```

This preallocates space.

Advantages:

- fewer reallocations
- fewer memory copies
- better performance

Very important for performance-sensitive code.

## Clearing the List

```zig
list.clearRetainingCapacity();
```

This removes all items but keeps the allocated memory.

Useful when reusing buffers repeatedly.

Example:

```text
Before:
len = 1000
capacity = 2048

After clear:
len = 0
capacity = 2048
```

No new allocation needed later.

## Converting to Owned Slice

Sometimes you want the final buffer itself.

```zig
const slice = try list.toOwnedSlice();
```

After this:

- the caller owns the memory
- the list becomes empty
- the caller must free the slice later

This is common in parsers and builders.

## ArrayList of Strings

You can store more than integers.

Example:

```zig
var list = std.ArrayList([]const u8).init(allocator);
```

Now each item is a string slice.

Example:

```zig
try list.append("apple");
try list.append("banana");
```

Loop:

```zig
for (list.items) |item| {
    std.debug.print("{s}\n", .{item});
}
```

Output:

```text
apple
banana
```

## Common Beginner Mistake: Forgetting `deinit`

Wrong:

```zig
var list = std.ArrayList(i32).init(allocator);
```

If you never call:

```zig
list.deinit();
```

you leak memory.

Always pair:

```zig
init()
```

with:

```zig
deinit()
```

Usually with `defer`.

## Common Beginner Mistake: Keeping Old Pointers

This is dangerous:

```zig
const ptr = &list.items[0];
try list.append(999);
```

Appending may reallocate memory.

If reallocation happens, `ptr` may point to invalid memory.

This is a very important concept.

Pointers into an `ArrayList` can become invalid after resizing.

## Internal Mental Model

A simplified internal structure looks like this:

```zig
const ArrayList = struct {
    ptr: [*]T,
    len: usize,
    capacity: usize,
};
```

Not exact implementation, but close enough conceptually.

The list owns a heap buffer:

```text
ptr ---> [10][20][30][ ]
```

`len` tracks used items.

`capacity` tracks allocated space.

## Why ArrayList Matters

`ArrayList` appears everywhere in Zig programs.

It is used for:

- dynamic strings
- parsers
- token lists
- file buffers
- HTTP requests
- JSON processing
- compiler internals
- game entities
- network packets

If you understand `ArrayList`, you understand one of the core patterns of Zig programming:

- explicit memory ownership
- allocator-driven design
- slices as views into memory
- manual lifetime management
- predictable performance

This is not just a container.

It teaches the philosophy of Zig itself.

