<h2>Arrays: A Estrutura Fixa de Dados</h2>
<p>Arrays são coleções de tamanho fixo que armazenam elementos do mesmo tipo. Em Go, quando você declara um array, você está comprometendo-se com um tamanho específico que não pode ser alterado após a criação. Isso oferece previsibilidade de memória e performance, mas também limitações que você precisa compreender desde o início.</p>
<p>A sintaxe para declarar um array em Go é <code>[tamanho]Tipo</code>. O tamanho é parte da definição do tipo, o que significa que <code>[5]int</code> e <code>[10]int</code> são tipos completamente diferentes. Isso é uma diferença fundamental em relação a linguagens como Python ou JavaScript. Quando você cria um array, todos os elementos são inicializados com o valor zero do tipo (0 para números, "" para strings, false para booleans, etc.).</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// Declarando um array de 5 inteiros
var numeros [5]int
fmt.Println(numeros) // Output: [0 0 0 0 0]
// Inicializando com valores
idades := [3]int{25, 30, 35}
fmt.Println(idades) // Output: [25 30 35]
// Usando ... para inferir tamanho
nomes := [...]string{"Alice", "Bob", "Charlie"}
fmt.Println(len(nomes)) // Output: 3
// Acessando e modificando elementos
idades[1] = 31
fmt.Println(idades[1]) // Output: 31
// Iterando sobre um array
for i, valor := range idades {
fmt.Printf("Índice %d: %d\n", i, valor)
}
}</code></pre>
<p>A principal vantagem dos arrays é a performance. Como o tamanho é conhecido em tempo de compilação, Go pode alocar a memória de forma contígua e eficiente. No entanto, essa rigidez torna os arrays inadequados para situações onde você não sabe antecipadamente quantos elementos precisará armazenar. Para esses casos, você usará <strong>slices</strong>, que é o tópico que faz a maioria dos iniciantes em Go finalmente entender quando usar qual estrutura.</p>
<h3>Quando Usar Arrays</h3>
<p>Use arrays quando o tamanho é conhecido e não muda. Exemplos práticos incluem: um baralho com 52 cartas, uma matriz de pixels em uma imagem fixa, ou configurações do sistema que nunca variam. Na prática industrial, você verá poucos arrays puros; a maioria das aplicações usa slices porque oferecem flexibilidade sem sacrificar performance significativa.</p>
<h2>Slices: A Flexibilidade que Go Oferece</h2>
<p>Slices são visões dinâmicas sobre um array subjacente. Eles não armazenam dados; apenas apontam para dados contidos em um array. Compreender essa distinção é crucial para dominar Go. Um slice consiste em três componentes internos: um ponteiro para o primeiro elemento, o comprimento (quantidade de elementos) e a capacidade (espaço disponível no array subjacente).</p>
<p>A sintaxe para declarar um slice é <code>[]Tipo</code> — note a ausência do tamanho, que é justamente o que o diferencia de um array. Você pode criar um slice de várias formas: usando uma literal, cortando um array existente, ou usando a função <code>make()</code>. Cada abordagem tem seu propósito específico.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// 1. Literal de slice
frutas := []string{"maçã", "banana", "laranja"}
fmt.Println(frutas) // Output: [maçã banana laranja]
fmt.Println(len(frutas)) // Output: 3
fmt.Println(cap(frutas)) // Output: 3
// 2. Criando com make (comprimento 0, capacidade 5)
numeros := make([]int, 0, 5)
fmt.Println(len(numeros)) // Output: 0
fmt.Println(cap(numeros)) // Output: 5
// 3. Cortando um array
array := [...]int{10, 20, 30, 40, 50}
slice := array[1:4] // Índices 1, 2, 3
fmt.Println(slice) // Output: [20 30 40]
fmt.Println(len(slice)) // Output: 3
fmt.Println(cap(slice)) // Output: 4 (capacidade é 5 - 1)
// 4. Adicionando elementos com append
numeros = append(numeros, 100)
fmt.Println(numeros) // Output: [100]
fmt.Println(cap(numeros)) // Output: 5
// 5. Slice com make e comprimento inicial
letras := make([]string, 3, 5)
letras[0] = "a"
letras[1] = "b"
letras[2] = "c"
fmt.Println(letras) // Output: [a b c]
}</code></pre>
<p>A função <code>append()</code> é onde a dinâmica dos slices se manifesta. Quando você adiciona elementos a um slice que não tem capacidade, Go automaticamente cria um novo array subjacente maior (geralmente dobrando a capacidade) e copia os dados existentes. Isso é transparente para você, mas compreender esse mecanismo explica por que algumas operações de append são mais caras que outras. Se você sabe antecipadamente quantos elementos precisará, criar um slice com <code>make([]T, 0, capacidade)</code> evita realocações desnecessárias.</p>
<h3>Cortando Slices</h3>
<p>O operador de corte <code>[inicio:fim]</code> cria um novo slice que compartilha o mesmo array subjacente. Modificações no novo slice afetam o slice original se referenciarem os mesmos elementos. Esse comportamento pode ser perigoso se não for compreendido, mas é extremamente poderoso quando bem utilizado.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
numeros := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Cortando do índice 2 até 7 (não inclui 7)
slice1 := numeros[2:7]
fmt.Println(slice1) // Output: [3 4 5 6 7]
// Modificando o slice1 modifica o array original
slice1[0] = 999
fmt.Println(numeros) // Output: [1 2 999 4 5 6 7 8 9 10]
// Corte omitindo o início
slice2 := numeros[:3]
fmt.Println(slice2) // Output: [1 2 999]
// Corte omitindo o fim
slice3 := numeros[6:]
fmt.Println(slice3) // Output: [7 8 9 10]
// Corte com capacidade
slice4 := numeros[2:5:8] // inicio:fim:capacidade
fmt.Println(slice4) // Output: [999 4 5]
fmt.Println(cap(slice4)) // Output: 6 (8 - 2)
}</code></pre>
<h2>Maps: Associações Chave-Valor</h2>
<p>Maps são estruturas de dados que associam chaves a valores. Diferentemente de arrays e slices (indexados por posição), maps usam chaves arbitrárias. Em Go, a sintaxe é <code>map[TipoChave]TipoValor</code>. As chaves devem ser de um tipo que suporte comparação de igualdade (números, strings, booleanos, etc.), enquanto os valores podem ser de qualquer tipo.</p>
<p>Um aspecto crucial: maps em Go são <strong>não ordenados</strong>. Cada vez que você itera sobre um map, a ordem dos elementos é aleatória. Isso é uma decisão deliberada do design da linguagem e diferencia Go de algumas outras linguagens. Se você precisa de uma ordem garantida, deve usar outras estruturas ou manter uma slice de chaves ordenadas separadamente.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// 1. Declarando e inicializando um map
pessoa := map[string]string{
"nome": "Alice",
"cidade": "São Paulo",
"país": "Brasil",
}
fmt.Println(pessoa) // Output: map[cidade:São Paulo país:Brasil nome:Alice]
// 2. Criando um map vazio com make
contagem := make(map[string]int)
contagem["maçã"] = 5
contagem["banana"] = 3
contagem["laranja"] = 8
fmt.Println(contagem) // Output: map[laranja:8 maçã:5 banana:3]
// 3. Acessando valores
fmt.Println(pessoa["nome"]) // Output: Alice
// 4. Verificando se uma chave existe
valor, existe := pessoa["idade"]
fmt.Println(valor, existe) // Output: false
valor, existe = pessoa["nome"]
fmt.Println(valor, existe) // Output: Alice true
// 5. Iterando sobre um map
for chave, valor := range pessoa {
fmt.Printf("%s: %s\n", chave, valor)
}
// 6. Deletando uma chave
delete(pessoa, "país")
fmt.Println(pessoa) // Output: map[cidade:São Paulo nome:Alice]
// 7. Comprimento de um map
fmt.Println(len(pessoa)) // Output: 2
}</code></pre>
<p>Maps são particularmente úteis para cachês, contadores, índices rápidos e associações dinâmicas. A busca por chave em um map é O(1) em média, tornando-os ideais para quando você precisa de acesso rápido por algum identificador. A limitação é que não há ordenamento garantido e eles usam mais memória que slices equivalentes.</p>
<h3>Maps de Tipos Complexos</h3>
<p>Você pode ter maps de praticamente qualquer coisa. Um padrão comum é maps de slices para construir índices invertidos, ou maps de structs para representar entidades complexas. Isso oferece uma flexibilidade poderosa, mas exige disciplina para não criar estruturas caóticas.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// Map de strings para slices de inteiros
grupos := map[string][]int{
"pares": {2, 4, 6, 8},
"ímpares": {1, 3, 5, 7},
"zeros": {0},
}
for chave, valores := range grupos {
fmt.Printf("%s: %v\n", chave, valores)
}
// Adicionando elementos a uma slice dentro do map
grupos["pares"] = append(grupos["pares"], 10)
fmt.Println(grupos["pares"]) // Output: [2 4 6 8 10]
// Map de strings para structs
type Usuario struct {
ID int
Email string
}
usuarios := map[string]Usuario{
"alice": {ID: 1, Email: "alice@example.com"},
"bob": {ID: 2, Email: "bob@example.com"},
}
fmt.Println(usuarios["alice"].Email) // Output: alice@example.com
}</code></pre>
<h2>Padrões Práticos e Boas Práticas</h2>
<p>Agora que você compreende os três pilares das coleções em Go, é essencial saber quando aplicá-los. Arrays são raros em código de produção; você os verá em situações muito específicas como buffers fixos ou implementações de estruturas de baixo nível. Slices são as rainhas das coleções em Go — use-as como padrão quando precisar de coleções dinâmicas. Maps devem ser usados para associações lógicas, não como arrays associativos genéricos.</p>
<p>Um padrão importante é o uso de slices de structs para representar coleções de objetos relacionados. Por exemplo, uma lista de usuários ou transações. Esse padrão combina a eficiência de slices com a estrutura de dados que structs oferecem. Outro padrão comum é usar maps para caches ou índices rápidos sobre dados já armazenados em slices.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// Padrão: Slice de structs com índice em map
type Produto struct {
ID int
Nome string
Preço float64
}
// Armazenamento principal
produtos := []Produto{
{ID: 1, Nome: "Laptop", Preço: 2500.00},
{ID: 2, Nome: "Mouse", Preço: 50.00},
{ID: 3, Nome: "Teclado", Preço: 150.00},
}
// Índice para busca rápida
indice := make(map[int]int) // ID -> índice no slice
for i, p := range produtos {
indice[p.ID] = i
}
// Buscando um produto
id := 2
if idx, existe := indice[id]; existe {
fmt.Println(produtos[idx]) // Output: {2 Mouse 50}
}
// Filtrando produtos por critério
func() {
var produtosCaros []Produto
for _, p := range produtos {
if p.Preço > 100 {
produtosCaros = append(produtosCaros, p)
}
}
fmt.Println(produtosCaros)
}()
// Contadores com map
nomes := []string{"Alice", "Bob", "Alice", "Charlie", "Bob", "Alice"}
frequencia := make(map[string]int)
for _, nome := range nomes {
frequencia[nome]++
}
fmt.Println(frequencia) // Output: map[Alice:3 Bob:2 Charlie:1]
}</code></pre>
<p>Uma consideração importante é a cópia de dados. Slices e maps são cópias superficiais — quando você atribui um slice a uma variável, você está copiando o cabeçalho (ponteiro, comprimento, capacidade), não os dados. Isso torna operações de cópia eficientes, mas você precisa estar ciente de que modificações afetam o original. Para uma cópia profunda de um slice, use <code>copy()</code> ou <code>append()</code> com inicialização.</p>
<pre><code class="language-go">package main
import "fmt"
func main() {
// Cópia superficial vs profunda
original := []int{1, 2, 3, 4, 5}
// Superficial - compartilha dados
superficial := original
superficial[0] = 999
fmt.Println(original) // Output: [999 2 3 4 5]
// Profunda com make
original2 := []int{1, 2, 3, 4, 5}
profunda := make([]int, len(original2))
copy(profunda, original2)
profunda[0] = 888
fmt.Println(original2) // Output: [1 2 3 4 5]
fmt.Println(profunda) // Output: [888 2 3 4 5]
}</code></pre>
<h2>Referências</h2>
<ul>
<li><a href="https://go.dev/ref/spec#Array_types" target="_blank" rel="noopener noreferrer">Go Spec - Arrays</a></li>
<li><a href="https://go.dev/ref/spec#Slice_types" target="_blank" rel="noopener noreferrer">Go Spec - Slices</a></li>
<li><a href="https://go.dev/ref/spec#Map_types" target="_blank" rel="noopener noreferrer">Go Spec - Maps</a></li>
<li><a href="https://go.dev/doc/effective_go#arrays" target="_blank" rel="noopener noreferrer">Effective Go - Arrays, slices, and maps</a></li>
<li><a href="https://go.dev/blog/slices-intro" target="_blank" rel="noopener noreferrer">The Go Blog - Slices: usage and internals</a></li>
</ul>
<p><!-- FIM --></p>