Introdução
Em muitas situações, você precisa de uma coleção que cresce e diminui dinamicamente durante a execução do programa. Arrays fixos em Zig têm tamanho definido em tempo de compilação, o que nem sempre é suficiente. Para isso, a biblioteca padrão oferece std.ArrayList, uma estrutura que gerencia automaticamente a realocação de memória conforme novos elementos são adicionados.
Nesta receita, você aprenderá a criar, manipular e iterar sobre ArrayLists em Zig, cobrindo as operações mais comuns do dia a dia.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
- Familiaridade com alocadores de memória
Criar e Popular um ArrayList
O exemplo mais básico: criar um ArrayList, adicionar elementos e iterar sobre eles.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar ArrayList de inteiros
var lista = std.ArrayList(i32).init(allocator);
defer lista.deinit();
// Adicionar elementos
try lista.append(10);
try lista.append(20);
try lista.append(30);
try lista.append(40);
try lista.append(50);
std.debug.print("Tamanho: {d}\n", .{lista.items.len});
std.debug.print("Capacidade: {d}\n", .{lista.capacity});
// Iterar sobre os elementos
std.debug.print("Elementos: ", .{});
for (lista.items) |item| {
std.debug.print("{d} ", .{item});
}
std.debug.print("\n", .{});
}
Saída esperada
Tamanho: 5
Capacidade: 8
Elementos: 10 20 30 40 50
Adicionar Múltiplos Elementos
Use appendSlice para adicionar vários elementos de uma vez:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var numeros = std.ArrayList(u32).init(allocator);
defer numeros.deinit();
// Adicionar slice de elementos
try numeros.appendSlice(&[_]u32{ 1, 2, 3, 4, 5 });
// Adicionar mais elementos individualmente
try numeros.append(6);
try numeros.append(7);
// Adicionar outro slice
const extras = [_]u32{ 8, 9, 10 };
try numeros.appendSlice(&extras);
std.debug.print("Total: {d} elementos\n", .{numeros.items.len});
for (numeros.items) |n| {
std.debug.print("{d} ", .{n});
}
std.debug.print("\n", .{});
}
Inserir e Remover Elementos
Inserir em posições específicas e remover elementos:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var lista = std.ArrayList([]const u8).init(allocator);
defer lista.deinit();
try lista.append("Banana");
try lista.append("Maçã");
try lista.append("Uva");
std.debug.print("Original: ", .{});
for (lista.items) |item| {
std.debug.print("{s} ", .{item});
}
std.debug.print("\n", .{});
// Inserir na posição 1
try lista.insert(1, "Laranja");
std.debug.print("Após inserir Laranja na posição 1: ", .{});
for (lista.items) |item| {
std.debug.print("{s} ", .{item});
}
std.debug.print("\n", .{});
// Remover por índice (orderedRemove mantém a ordem)
_ = lista.orderedRemove(0);
std.debug.print("Após remover posição 0: ", .{});
for (lista.items) |item| {
std.debug.print("{s} ", .{item});
}
std.debug.print("\n", .{});
// swapRemove é O(1) mas não mantém a ordem
_ = lista.swapRemove(0);
std.debug.print("Após swapRemove posição 0: ", .{});
for (lista.items) |item| {
std.debug.print("{s} ", .{item});
}
std.debug.print("\n", .{});
}
Pré-alocar Capacidade
Se você sabe quantos elementos vai precisar, pré-aloque para evitar realocações:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var lista = std.ArrayList(f64).init(allocator);
defer lista.deinit();
// Pré-alocar espaço para 1000 elementos
try lista.ensureTotalCapacity(1000);
std.debug.print("Capacidade inicial: {d}\n", .{lista.capacity});
// Adicionar 1000 elementos sem realocação
for (0..1000) |i| {
try lista.append(@as(f64, @floatFromInt(i)) * 1.5);
}
std.debug.print("Tamanho final: {d}\n", .{lista.items.len});
std.debug.print("Capacidade final: {d}\n", .{lista.capacity});
}
Converter entre ArrayList e Slice
const std = @import("std");
pub fn somaSlice(nums: []const i32) i64 {
var soma: i64 = 0;
for (nums) |n| {
soma += n;
}
return soma;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var lista = std.ArrayList(i32).init(allocator);
defer lista.deinit();
try lista.appendSlice(&[_]i32{ 10, 20, 30, 40, 50 });
// ArrayList.items é um slice — use diretamente
const resultado = somaSlice(lista.items);
std.debug.print("Soma: {d}\n", .{resultado});
// toOwnedSlice transfere a propriedade da memória
const owned = try lista.toOwnedSlice();
defer allocator.free(owned);
std.debug.print("Owned slice tamanho: {d}\n", .{owned.len});
// Após toOwnedSlice, a lista fica vazia
std.debug.print("Lista após toOwnedSlice: {d} elementos\n", .{lista.items.len});
}
Exemplo Prático: Coletar Números Pares
const std = @import("std");
fn coletarPares(allocator: std.mem.Allocator, numeros: []const i32) ![]i32 {
var pares = std.ArrayList(i32).init(allocator);
errdefer pares.deinit();
for (numeros) |n| {
if (@mod(n, 2) == 0) {
try pares.append(n);
}
}
return pares.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const dados = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
const pares = try coletarPares(allocator, &dados);
defer allocator.free(pares);
std.debug.print("Números pares: ", .{});
for (pares) |p| {
std.debug.print("{d} ", .{p});
}
std.debug.print("\n", .{});
std.debug.print("Total: {d}\n", .{pares.len});
}
Saída esperada
Números pares: 2 4 6 8 10 12
Total: 6
Dicas e Boas Práticas
Sempre libere a memória: Use
defer lista.deinit()logo após criar o ArrayList.Pré-aloque quando possível: Se souber o tamanho aproximado, use
ensureTotalCapacitypara evitar realocações.Use
toOwnedSlicecom cuidado: Após chamar, a lista fica vazia e a responsabilidade de liberar a memória passa para você.Prefira
orderedRemovequando a ordem importa:swapRemoveé mais rápido (O(1)) mas altera a ordem dos elementos.Use
errdefer: Ao construir um ArrayList em uma função que pode falhar, useerrdefer lista.deinit()para evitar vazamentos.
Receitas Relacionadas
- Como usar HashMap em Zig - Mapas chave-valor
- Como filtrar arrays em Zig - Filtrar elementos
- Como ordenar arrays em Zig - Ordenação de dados
- Usando GeneralPurposeAllocator - Alocador de memória