Introdução
Bem-vindos Gophers a este novo capítulo.
Neste capítulo, aprenderemos sobre o uso básico de mapas e analisaremos a presença de nil em Go.
Após concluir este capítulo, você será capaz de aplicar mapas em suas tarefas diárias de desenvolvimento.
Pontos de Conhecimento:
- Declarando um mapa
- Inicializando um mapa
- Adicionando, excluindo, atualizando e pesquisando elementos de um mapa
- Verificando se um elemento de mapa existe
- Iterando sobre um mapa
Introdução aos Dicionários
Então, o que é um dicionário?
Em ciência da computação, um dicionário é uma estrutura de dados especial que consiste em pares chave-valor (key-value pairs).
Par chave-valor: Um par de elementos, onde um elemento é chamado de chave (key) e o outro é chamado de valor (value).
Então, por que precisamos usar um dicionário?
Porque os dicionários têm alta eficiência quando se trata de operações como adicionar, excluir, atualizar e pesquisar elementos, acredito que todos vocês acharão esta estrutura de dados muito útil.

Declaração de Maps
Para uma nova estrutura de dados, o primeiro passo é aprender como declará-la.
Para um map, a forma de defini-lo é:
var variableName map[keyType]valueType
Por exemplo:
var a map[string]int
No entanto, como um map é um tipo de referência, se o declararmos como no código acima, encontraremos um problema.
Vamos criar um arquivo map.go no diretório ~/project:
touch ~/project/map.go
Escreva o seguinte código em map.go:
package main
func main() {
// Declare a map named m with key type as string and value type as int
var m map[string]int
// Add a data entry "labex"->1 to it
m["labex"] = 1 // Program crashes
}
Execute o programa:
go run map.go
Descobrimos que o programa travou e lançou um erro:
panic: assignment to entry in nil map
Isso significa que atribuir um elemento a um map nil é um comportamento errôneo.
Isso ocorre porque, para estruturas de dados como slices (fatias), maps, channels (canais) e pointers (ponteiros), elas devem ser inicializadas antes do uso.
E nil é o valor padrão inicial para um mapa, o que significa que nenhuma memória é alocada para a variável durante a definição.
O Valor Inicial nil
Na seção anterior, mencionamos que é um comportamento errôneo atribuir um elemento a um mapa nil.
Vamos aproveitar esta oportunidade para explorar o verdadeiro significado de nil em Go.
A essência de nil é um identificador pré-declarado.
Para tipos de dados básicos, seus valores iniciais são diferentes:
- Valores booleanos
- Valores numéricos
- Strings
No entanto, para slices (fatias), dicionários, ponteiros, channels (canais) e funções, seu valor inicial é nil. Não é o valor padrão inicial com o qual estamos familiarizados.
Isso se reflete nos objetos de instanciação que recebem nil. Embora possam ser impressos, eles não podem ser usados.
Além disso, há alguns pontos a serem observados sobre nil.
Incomparabilidade de Diferentes Tipos de nil
package main
import "fmt"
func main() {
var m map[int]string
var p *int
fmt.Printf("%v", m == p)
}
A saída do programa é a seguinte:
invalid operation: m == p (mismatched types map[int]string and *int)
Ou seja, não podemos comparar o nil de um ponteiro do tipo int com o nil de um mapa.
Eles não são comparáveis.
nil não é uma palavra-chave (keyword)
package main
import "fmt"
func main() {
var nilValue = "= =$"
fmt.Println(nilValue)
}
A saída do programa é a seguinte:
= =$
Podemos definir uma variável chamada nil e ela pode ser compilada sem erros. No entanto, recomendamos fortemente que você não faça isso no desenvolvimento real.
Incomparabilidade de nil
package main
import "fmt"
func main() {
fmt.Println(nil == nil)
}
A saída do programa é a seguinte:
invalid operation: nil == nil (operator == not defined on nil)
Declaração com a Palavra-chave make
Depois de entender o significado de nil, vamos resolver o problema da alocação de memória para dicionários.
Aqui, usamos a palavra-chave make para alocar memória.
A função make gera um valor de um tipo especificado que foi inicializado como seu valor de retorno.
package main
import "fmt"
func main() {
var m map[string]int // Declare a dictionary
m = make(map[string]int) // Allocate memory to the dictionary
m["labex"] = 1 // Add data to the dictionary
fmt.Println(m)
}
A saída do programa é a seguinte:
map[labex:1]
O código acima demonstra como alocar memória para um dicionário que foi declarado usando a palavra-chave make.
Também podemos simplificar este processo:
Escreva o seguinte código em map.go:
package main
import "fmt"
func main() {
m := make(map[string]int) // Declare and initialize a dictionary
m["labex"] = 666 // Add data to the dictionary
fmt.Println(m)
}
A saída do programa é a seguinte:
map[labex:666]
O código acima mostra como declarar um dicionário usando a palavra-chave make.
Inicialização Manual de um Map Vazio
Na seção anterior, demonstramos como usar a palavra-chave make para inicializar um mapa. Agora, vamos explorar outro método: usar a sintaxe literal para inicializar manualmente um mapa vazio.
Este método é conciso e permite criar um mapa vazio que está pronto para uso sem alocar memória explicitamente.
Escreva o seguinte código em map.go:
package main
import "fmt"
func main() {
// Using literal syntax to initialize an empty map
m := map[string]int{}
// Adding elements to the map
m["labex"] = 777
m["labs"] = 11
fmt.Println(m) // Output the map with added elements
}
A sintaxe map[keyType]valueType{} cria um mapa vazio pronto para uso. Uma vez inicializado, você pode adicionar elementos ao mapa usando a sintaxe map[chave] = valor.
Quando você executa o código acima, o programa exibirá:
map[labex:777 labs:11]
Benefícios da Inicialização Manual:
- Fornece uma maneira rápida de criar um mapa vazio sem usar a palavra-chave
make. - Útil quando você precisa começar com um mapa vazio e populá-lo dinamicamente.
Inicializando o Dicionário de Fato
Como podemos inicializar um dicionário vazio, também podemos fornecer alguns valores iniciais.
Escreva o seguinte código em map.go:
package main
import "fmt"
func main() {
m := map[string]int{
"labex": 777,
"labs": 11,
}
m["labby"] = 666
fmt.Println(m)
}
A saída do programa é a seguinte:
map[labby:666 labs:11 labex:777]
Observe que, ao inicializar um dicionário em Go, uma vírgula deve ser adicionada após cada elemento, incluindo o último.
Adicionando e Atualizando Elementos do Dicionário
Adicionar elementos a um dicionário é muito simples, como demonstrado no exemplo de código acima.
A sintaxe é:
dictionaryInstance[keyToAdd] = valueToAdd
Por exemplo:
m := map[string]int{}
m["labby"] = 666
Observação: Em Go, cada key em um map deve ser único.
Escreva o seguinte código em map.go:
package main
import "fmt"
func main() {
m := map[string]int{}
m["labby"] = 666
m["labby"] = 777
fmt.Println(m)
}
A saída do programa é a seguinte:
map[labby:777]
Descobrimos que o valor da saída foi alterado para 777. Ou seja, se escrevermos valores diferentes para a mesma key, o value correspondente será atualizado para o novo valor.
É assim que atualizamos ou modificamos um dicionário.
Excluindo Elementos do Dicionário
Como podemos excluir um elemento de um dicionário?
Precisamos usar a função delete embutida.
Escreva o seguinte código em map.go:
package main
import "fmt"
func main() {
m := map[string]int{
"labex": 777,
"labs": 11,
"labby": 666,
}
fmt.Println(m)
delete(m, "labs")
fmt.Println(m)
}
A saída do programa é a seguinte:
map[labby:666 labs:11 labex:777]
map[labby:666 labex:777]
A função delete recebe dois argumentos. O primeiro argumento é o dicionário a ser operado, e o segundo argumento é a chave a ser excluída.
Claro, a função delete tem outros usos, mas discutiremos apenas seu uso em dicionários neste laboratório.
Pesquisando Elementos em Dicionários
O que acontece quando procuramos por um elemento em um dicionário que não existe?
package main
import "fmt"
func main() {
m := map[string]int{
"labex": 0,
}
fmt.Print("O valor de labs é: ")
fmt.Println(m["labs"])
}
A saída do programa é a seguinte:
O valor de labs é: 0
Descobrimos que, se o elemento não existir no dicionário, consultá-lo retornará o valor padrão do tipo de valor.
E quanto à chave no dicionário cujo valor é 0?
package main
import "fmt"
func main() {
m := map[string]int{
"labex": 0,
}
fmt.Print("O valor de labs é: ")
fmt.Println(m["labs"])
fmt.Print("O valor de labex é: ")
fmt.Println(m["labex"])
}
A saída do programa é a seguinte:
O valor de labs é: 0
O valor de labex é: 0
Descobrimos que, para um dicionário, a apresentação de um valor que não existe e um valor que existe, mas tem um valor padrão, é a mesma.
Isso nos causa muita confusão. Como resolvemos isso?
Acontece que os desenvolvedores do Go já haviam pensado nesse problema. Quando consultamos um elemento em um dicionário, haverá um ou dois valores de retorno de variável.
Ou seja:
labs, ok := m["labs"]
Modifique map.go:
package main
import "fmt"
func main() {
m := map[string]int{
"labex": 0,
}
labs, ok := m["labs"]
fmt.Print("O valor de labs é: ")
fmt.Print(labs)
fmt.Print(" Ele existe? ")
fmt.Println(ok)
labex, ok2 := m["labex"]
fmt.Print("O valor de labex é: ")
fmt.Print(labex)
fmt.Print(" Ele existe? ")
fmt.Println(ok2)
}
A saída do programa é a seguinte:
O valor de labs é: 0 Ele existe? false
O valor de labex é: 0 Ele existe? true
Agora, podemos usar o segundo valor de retorno da consulta para determinar se o resultado retornado é um valor padrão inicial existente ou um valor padrão inicial inexistente.
Iterando sobre Dicionários
Em certos cenários, precisamos iterar sobre um dicionário inteiro, consultar cada par chave-valor (key-value pair) e processá-los. Como podemos conseguir isso?
package main
import (
"fmt"
)
func main() {
m := map[string]int{
"labex": 777,
"labs": 11,
"labby": 666,
}
for key, value := range m {
fmt.Println(key, value)
}
}
A saída do programa é a seguinte:
labby 666
labs 11
labex 777
Pela saída, podemos ver que a maneira de iterar sobre um dicionário é muito semelhante à iteração sobre fatias (slices) ou arrays.
Resumo
Neste laboratório, aprendemos sobre o uso fundamental de dicionários, incluindo:
- A declaração de dicionários e como abordar a questão da declaração
- As características do valor inicial
nil - Os métodos de inicialização de dicionários
- Adição, remoção, atualização e busca de elementos em dicionários
- Detecção da existência de elementos de dicionário
- Iteração sobre dicionários



