Map
Map
Imagine consultar um dicionário: você insere uma palavra (chave) e pode encontrar imediatamente sua definição (valor). Você não precisa folhear da primeira à última página — vai diretamente à página alvo. O map do Go é exatamente esse "dicionário" — é uma estrutura de dados chave-valor que permite buscar rapidamente um valor por chave com complexidade de tempo O(1).
1. Conceitos Fundamentais
| Conceito | Descrição |
|---|---|
| Definição | map[KeyType]ValueType; KeyType deve ser um tipo comparável (não pode ser slice, map, func) |
| Criação | make(map[K]V) ou literal map[K]V{} |
| Adicionar/Atualizar | m[chave] = valor (adiciona se a chave não existe, sobrescreve se existe) |
| Ler | v := m[chave] |
| Deletar | delete(m, chave) |
| Padrão comma ok | v, ok := m[chave]; ok é falso quando a chave não existe |
| Iteração | for k, v := range m (ordem aleatória, não confiável) |
| Comprimento | len(m) |
2. Sintaxe/Uso Básico
Criando um Map
// Método 1: Usando make
m1 := make(map[string]int)
// Método 2: Criação literal com inicialização
m2 := map[string]int{
"maca": 5,
"banana": 3,
}
// Método 3: Declarar e depois atribuir
var m3 map[string]int // m3 é nil aqui, não pode atribuir diretamente
m3 = make(map[string]int) // Inicializar primeiro
m3["cereja"] = 7 // Depois atribuir
var m map[K]V é nil — escrever nele causará panic, mas ler dele (retorna valor zero) e len() (retorna 0) funcionam normalmente.
Operações CRUD
m := map[string]int{"a": 1, "b": 2}
// Criar
m["c"] = 3
// Atualizar
m["a"] = 10
// Ler
v := m["a"] // v = 10
// Deletar
delete(m, "b")
Padrão Comma Ok
m := map[string]int{"x": 42}
v, ok := m["x"] // v = 42, ok = true
v, ok = m["y"] // v = 0, ok = false (retorna o valor zero de int)
if _, existe := m["z"]; !existe {
fmt.Println("chave 'z' não existe")
}
_ para ignorá-lo: _, ok := m[chave].
3. Código de Exemplo
Exemplo 1: Uso Básico (Dificuldade ⭐)
Crie uma tabela de notas de estudantes e realize operações CRUD.
package main
import "fmt"
func main() {
// Criar map de notas dos estudantes
notas := map[string]int{
"Alice": 90,
"Bob": 85,
}
// Adicionar um estudante
notas["Charlie"] = 92
// Modificar nota do Bob
notas["Bob"] = 88
// Consultar nota da Alice
fmt.Println("Nota da Alice:", notas["Alice"])
// Deletar Charlie
delete(notas, "Charlie")
// Iterar todas as notas dos estudantes
for nome, nota := range notas {
fmt.Printf("%s: %d\n", nome, nota)
}
fmt.Println("Número de estudantes:", len(notas))
}
Exemplo de saída (a ordem de iteração pode variar):
Nota da Alice: 90
Bob: 88
Alice: 90
Número de estudantes: 2
Exemplo 2: Uso Intermediário (Dificuldade ⭐⭐)
Consulta segura com padrão comma ok, iteração de maps e maps aninhados.
package main
import "fmt"
func main() {
// ========== Padrão comma ok ==========
frutas := map[string]int{
"maca": 5,
"banana": 3,
}
// Consulta segura
if quantidade, ok := frutas["maca"]; ok {
fmt.Printf("maca tem %d\n", quantidade)
}
if quantidade, ok := frutas["uva"]; !ok {
fmt.Println("uva não existe, adicionando à lista")
frutas["uva"] = 10
}
// ========== Iteração com range ==========
fmt.Println("\nTodas as frutas:")
for fruta, quantidade := range frutas {
fmt.Printf(" %s: %d\n", fruta, quantidade)
}
// ========== Map aninhado (map de maps) ==========
// Turma -> Estudante -> Nota
notasTurma := map[string]map[string]int{
"Turma A": {
"Alice": 90,
"Bob": 85,
},
"Turma B": {
"Charlie": 92,
"Diana": 88,
},
}
// Consultar map aninhado
if turma, ok := notasTurma["Turma A"]; ok {
if nota, ok := turma["Alice"]; ok {
fmt.Printf("\nNota da Alice na Turma A: %d\n", nota)
}
}
// Iterar map aninhado
fmt.Println("\nNotas de todas as turmas:")
for turma, estudantes := range notasTurma {
fmt.Printf(" %s:\n", turma)
for nome, nota := range estudantes {
fmt.Printf(" %s: %d\n", nome, nota)
}
}
}
Saída:
maca tem 5
uva não existe, adicionando à lista
Todas as frutas:
maca: 5
banana: 3
uva: 10
Nota da Alice na Turma A: 90
Notas de todas as turmas:
Turma A:
Alice: 90
Bob: 85
Turma B:
Charlie: 92
Diana: 88
Exemplo 3: Aplicação Abrangente (Dificuldade ⭐⭐⭐)
Implemente um contador de frequência de palavras com processamento de dados baseado em map (saída ordenada, encontrar palavras de alta frequência).
package main
import (
"fmt"
"sort"
"strings"
)
// WordCounter conta ocorrências de cada palavra em um texto
func WordCounter(texto string) map[string]int {
// Converter para minúsculas e dividir por espaços em branco
palavras := strings.Fields(strings.ToLower(texto))
// Criar map de frequência de palavras
freq := make(map[string]int)
for _, palavra := range palavras {
// Remover pontuação (processamento simples)
palavra = strings.Trim(palavra, ".,!?;:\"'")
if palavra != "" {
freq[palavra]++
}
}
return freq
}
// TopN retorna as N palavras mais frequentes (ordem decrescente de frequência)
func TopN(freq map[string]int, n int) []string {
// Converter map para slice ordenável
type wordFreq struct {
word string
count int
}
// Construir slice
pares := make([]wordFreq, 0, len(freq))
for w, c := range freq {
pares = append(pares, wordFreq{w, c})
}
// Ordenar por frequência decrescente
sort.Slice(pares, func(i, j int) bool {
if pares[i].count == pares[j].count {
return pares[i].word < pares[j].word // Alfabético quando frequência é igual
}
return pares[i].count > pares[j].count
})
// Pegar top N
if n > len(pares) {
n = len(pares)
}
resultado := make([]string, n)
for i := 0; i < n; i++ {
resultado[i] = fmt.Sprintf("%s(%d)", pares[i].word, pares[i].count)
}
return resultado
}
// MergeFreq mescla dois maps de frequência de palavras
func MergeFreq(a, b map[string]int) map[string]int {
resultado := make(map[string]int)
// Copiar dados de a
for k, v := range a {
resultado[k] = v
}
// Acumular dados de b
for k, v := range b {
resultado[k] += v
}
return resultado
}
func main() {
texto1 := "Go é ótimo. Go é rápido. Go é fácil de aprender."
texto2 := "Eu amo Go. Go torna a programação divertida e fácil."
// Contar frequência de palavras
freq1 := WordCounter(texto1)
freq2 := WordCounter(texto2)
fmt.Println("Frequência de palavras do Texto 1:")
for palavra, count := range freq1 {
fmt.Printf(" %s: %d\n", palavra, count)
}
fmt.Println("\nFrequência de palavras do Texto 2:")
for palavra, count := range freq2 {
fmt.Printf(" %s: %d\n", palavra, count)
}
// Mesclar frequências de palavras
merged := MergeFreq(freq1, freq2)
// Encontrar as 5 palavras mais frequentes
fmt.Println("\nTop 5 palavras de alta frequência após mesclagem:")
top5 := TopN(merged, 5)
for i, item := range top5 {
fmt.Printf(" %d. %s\n", i+1, item)
}
// Contar total de palavras
totalPalavras := 0
for _, count := range merged {
totalPalavras += count
}
fmt.Printf("\nTotal de palavras: %d, Palavras únicas: %d\n", totalPalavras, len(merged))
}
Exemplo de saída:
Frequência de palavras do Texto 1:
go: 3
é: 3
ótimo: 1
rápido: 1
fácil: 1
de: 1
aprender: 1
Frequência de palavras do Texto 2:
eu: 1
amo: 1
go: 2
torna: 1
a: 1
programação: 1
divertida: 1
e: 1
fácil: 1
Top 5 palavras de alta frequência após mesclagem:
1. go(5)
2. é(3)
3. fácil(2)
4. a(1)
5. amo(1)
Total de palavras: 18, Palavras únicas: 13
3. Casos de Uso Comuns
Caso 1: Cache / Busca Rápida
Use um map para implementar um cache simples em memória para evitar computação redundante.
package main
import "fmt"
// Sequência de Fibonacci (com cache)
var cache = map[int]int{0: 0, 1: 1}
func fib(n int) int {
// Verificar cache primeiro
if val, ok := cache[n]; ok {
return val
}
// Cache miss, computar e armazenar no cache
resultado := fib(n-1) + fib(n-2)
cache[n] = resultado
return resultado
}
func main() {
for i := 0; i <= 10; i++ {
fmt.Printf("fib(%d) = %d\n", i, fib(i))
}
fmt.Println("\nConteúdo do cache:", cache)
}
Caso 2: Estatísticas de Agrupamento
Use um map para agrupar e contar dados.
package main
import "fmt"
func main() {
// Lista de estudantes: nome -> turma
estudantes := map[string]string{
"Alice": "Turma A",
"Bob": "Turma B",
"Charlie": "Turma A",
"Diana": "Turma B",
"Eve": "Turma A",
}
// Agrupar por turma
grupos := make(map[string][]string)
for nome, turma := range estudantes {
grupos[turma] = append(grupos[turma], nome)
}
// Imprimir resultados agrupados
for turma, membros := range grupos {
fmt.Printf("%s (%d pessoas): %v\n", turma, len(membros), membros)
}
}
Saída:
Turma A (3 pessoas): [Alice Charlie Eve]
Turma B (2 pessoas): [Bob Diana]
❓ Perguntas Frequentes
P1: Por que a ordem de iteração de um map é diferente a cada vez?
Go intencionalmente randomiza a ordem de iteração do map. Isso impede que desenvolvedores dependam da ordem de iteração do map, já que a estrutura interna pode mudar em cenários concorrentes. Se precisar de iteração ordenada, colete as chaves em um slice primeiro, ordene-as e depois itere:
chaves := make([]string, 0, len(m))
for k := range m {
chaves = append(chaves, k)
}
sort.Strings(chaves)
for _, k := range chaves {
fmt.Println(k, m[k])
}
P2: Um map é thread-safe?
Não. O map do Go não é seguro para concorrência; múltiplas goroutines lendo e escrevendo no mesmo map simultaneamente causarão um panic. Soluções:
- Use
sync.Mutexousync.RWMutexpara proteger o map - Use
sync.Mapfornecido desde o Go 1.9 (adequado para cenários de muita leitura e pouca escrita)
// Usando sync.Map
var m sync.Map
m.Store("chave", "valor")
v, ok := m.Load("chave")
P3: Quais tipos podem ser usados como chaves de map?
Chaves de map devem ser tipos comparáveis, incluindo:
- Tipos básicos:
int,float64,string,bool - Ponteiros
- Arrays (com elementos comparáveis)
- Structs (com todos os campos comparáveis)
Tipos que não podem ser chaves: slice, map, func.
P4: Como verificar se um map contém determinada chave?
Use o padrão comma ok:
if _, ok := m[chave]; ok {
// Chave existe
} else {
// Chave não existe
}
Não verifique se uma chave existe testando se o valor é um valor zero, pois o valor zero pode ser um valor válido.
📖 Resumo
- map é a estrutura de dados chave-valor embutida do Go; busca rapidamente valores por chave
- Criado com
makeou literalmap[K]V{}; mapsnildeclarados comvarsão somente leitura - Adicionar/Atualizar:
m[chave] = valor; Ler:v := m[chave]; Deletar:delete(m, chave) - Use o padrão comma ok (
v, ok := m[chave]) para verificar com segurança se uma chave existe - Use
for k, v := range mpara iterar; a ordem é aleatória e não confiável - map é um tipo de referência; atribuição e passagem de parâmetros não copiam dados
- map não é seguro para concorrência; use locks ou
sync.Mappara leitura/escrita em múltiplas goroutines - map vs slice: use map para buscas frequentes, slice para armazenamento ordenado; os dois frequentemente são usados juntos
📝 Exercícios
Exercício 1 (⭐)
Crie um map armazenando 5 linguagens de programação e seus anos de invenção, depois:
- Adicione 2 novas linguagens
- Modifique o ano de uma linguagem
- Delete uma linguagem
- Use o padrão comma ok para verificar se uma linguagem existe
- Itere e imprima todo o conteúdo
Exercício 2 (⭐⭐)
Implemente um programa simples de contatos:
- Defina
map[string][]string, onde a chave é o nome do contato e o valor é a lista de números de telefone - Implemente funções: adicionar contato, adicionar número de telefone, buscar contato, deletar contato
- Implemente a exibição de todos os contatos agrupados pela primeira letra
// Exemplo de saída esperada:
// A: Alice - [13800001111, 13900002222]
// B: Bob - [13700003333]
Exercício 3 (⭐⭐⭐)
Implemente um sistema de gerenciamento de notas de estudantes:
- Use map aninhado
map[string]map[string]float64(turma -> estudante -> nota) - Implemente funções: adicionar nota, consultar todas as notas de disciplinas de um estudante, calcular média da turma
- Implemente função: encontrar o estudante com a nota mais alta em cada disciplina em todas as turmas
- Saída dos resultados como tabelas formatadas
// Exemplo de saída esperada:
// ========== Médias das Turmas ==========
// Turma A: 87.5
// Turma B: 91.2
//
// ========== Melhores Notas por Disciplina ==========
// Matemática: Alice (98.0)
// Inglês: Bob (95.0)



