Maps
Esta aula aborda maps em Go, incluindo criação, acesso, verificação de existência com comma-ok idiom, remoção de chaves com delete e iteração com ordem aleatória. O conteúdo inclui exemplos práticos e exercícios com respostas ocultas.
Maps em Go são estruturas de dados que associam chaves a valores, funcionando como tabelas hash ou dicionários em outras linguagens. Eles são extremamente úteis para armazenar e recuperar dados rapidamente com base em uma chave única. Nesta aula, você aprenderá a criar, acessar, verificar a existência de chaves, remover entradas e iterar sobre maps, além de entender a característica de ordem aleatória na iteração.
Diferente de slices, maps são referências implícitas: ao atribuir um map a outra variável ou passá-lo para uma função, ambas compartilham o mesmo dado subjacente. Por isso, é importante entender seu comportamento para evitar efeitos colaterais.
Criação e acesso
Para criar um map, usamos a sintaxe map[TipoChave]TipoValor. A chave deve ser um tipo comparável (como strings, inteiros, floats, booleanos ou structs com campos comparáveis). O valor pode ser qualquer tipo, incluindo outros maps ou slices.
Existem duas formas principais de criar um map: usando a função make ou uma literal de map. Com make, você especifica o tipo e, opcionalmente, a capacidade inicial. Com literais, você já pode inicializar com pares chave-valor.
package main
import "fmt"
func main() {
// Criando um map vazio com make
var m1 map[string]int // nil map, não pode ser usado diretamente
m1 = make(map[string]int) // inicializa
m1["chave1"] = 10
fmt.Println(m1) // map[chave1:10]
// Criando e inicializando com literal
m2 := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
fmt.Println(m2) // map[a:1 b:2 c:3]
// Acessando valores
valor := m2["a"]
fmt.Println(valor) // 1
// Acessando chave inexistente retorna valor zero do tipo
zero := m2["z"]
fmt.Println(zero) // 0
}
Note que acessar uma chave que não existe não causa erro; retorna o valor zero do tipo do valor. Isso pode ser enganoso, por isso o comma-ok idiom é importante.
Comma-ok idiom
Para verificar se uma chave realmente existe no map, usamos o comma-ok idiom. A atribuição de um map retorna dois valores: o valor (ou zero) e um booleano indicando se a chave estava presente.
package main
import "fmt"
func main() {
m := map[string]int{"a": 1, "b": 2}
valor, ok := m["a"]
if ok {
fmt.Println("Chave 'a' existe com valor", valor)
} else {
fmt.Println("Chave 'a' não existe")
}
valor, ok = m["z"]
if !ok {
fmt.Println("Chave 'z' não existe, valor retornado:", valor) // 0
}
}
Esse padrão é essencial para evitar confusão entre chave existente com valor zero e chave inexistente. Sempre que você espera que a chave possa não existir, use o comma-ok.
delete
A função embutida delete remove uma chave de um map. Sua sintaxe é delete(map, chave). Se a chave não existir, a operação é um no-op (não causa erro).
package main
import "fmt"
func main() {
m := map[string]int{"a": 1, "b": 2, "c": 3}
fmt.Println("Antes:", m)
delete(m, "b")
fmt.Println("Depois de deletar 'b':", m)
// Deletar chave inexistente não faz nada
delete(m, "z")
fmt.Println("Depois de tentar deletar 'z':", m)
}
Não há função para esvaziar um map completamente; você pode iterar e deletar todas as chaves, ou simplesmente atribuir um novo map vazio (se não houver outras referências).
Iteração (ordem aleatória)
Para iterar sobre um map, usamos range. A ordem de iteração não é garantida e pode variar entre execuções. Isso é intencional em Go para forçar o programador a não depender de uma ordem específica.
package main
import "fmt"
func main() {
m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}
for chave, valor := range m {
fmt.Printf("%s -> %d\n", chave, valor)
}
// Se quiser apenas as chaves:
for chave := range m {
fmt.Println("Chave:", chave)
}
}
Se você precisar de uma ordem fixa, pode coletar as chaves em um slice, ordená-lo e iterar sobre ele.
package main
import (
"fmt"
"sort"
)
func main() {
m := map[string]int{"b": 2, "a": 1, "c": 3}
chaves := make([]string, 0, len(m))
for k := range m {
chaves = append(chaves, k)
}
sort.Strings(chaves)
for _, k := range chaves {
fmt.Printf("%s -> %d\n", k, m[k])
}
}
Lembre-se: maps não são seguros para concorrência. Se você precisar acessar um map de múltiplas goroutines, use sincronização (como sync.Mutex) ou sync.Map.
Boas práticas
- Inicialize maps com
makeou literais para evitar pânico ao tentar escrever em um map nil. - Sempre use comma-ok ao acessar chaves que podem não existir.
- Não confie na ordem de iteração; se precisar de ordem, ordene as chaves.
- Para maps aninhados, lembre-se de inicializar os maps internos separadamente.
- Evite usar maps quando a ordem importa; considere slices de structs.
Referências
- Go Blog: Maps
- Go Specification: Map types
- Go Specification: Making slices, maps and channels
- Go Specification: Deletion of map elements
- Package builtin: delete
- Package sort
Exercícios
Crie um map de string para int com as chaves "Go", "Java", "Python" e valores 1, 2, 3 respectivamente. Imprima o valor associado a "Go".
✓ Resposta:package main import "fmt" func main() { linguagens := map[string]int{ "Go": 1, "Java": 2, "Python": 3, } fmt.Println(linguagens["Go"]) // 1 }Dado o map do exercício anterior, verifique se a chave "Ruby" existe usando comma-ok. Imprima uma mensagem adequada.
✓ Resposta:package main import "fmt" func main() { linguagens := map[string]int{"Go": 1, "Java": 2, "Python": 3} if _, ok := linguagens["Ruby"]; ok { fmt.Println("Ruby existe") } else { fmt.Println("Ruby não existe") } }Remova a chave "Java" do map do exercício anterior e imprima o map resultante.
✓ Resposta:package main import "fmt" func main() { linguagens := map[string]int{"Go": 1, "Java": 2, "Python": 3} delete(linguagens, "Java") fmt.Println(linguagens) // map[Go:1 Python:3] }Itere sobre o map do exercício 1 e imprima cada chave e valor em linhas separadas (não se preocupe com a ordem).
✓ Resposta:package main import "fmt" func main() { linguagens := map[string]int{"Go": 1, "Java": 2, "Python": 3} for chave, valor := range linguagens { fmt.Printf("%s: %d\n", chave, valor) } }Crie um map de string para slice de strings. Adicione três entradas e imprima o slice de uma delas.
✓ Resposta:package main import "fmt" func main() { categorias := map[string][]string{ "frutas": {"maçã", "banana"}, "legumes": {"cenoura", "batata"}, "grãos": {"arroz", "feijão"}, } fmt.Println(categorias["frutas"]) // [maçã banana] }