Ma

Mapas em Go

2 exercícios

Sobre Mapas

Em Go, o map é um tipo de dados incorporado que representa uma tabela de hash. Noutras linguagens de programação, podes conhecer o map como dict, array associativo ou armazenamento de chave/valor. Se não estás familiarizado com este conceito, podes pensar no map como um dicionário, em que cada palavra é a key e a definição é o element do map.

Antes de começarmos, gostaria de te indicar a especificação da linguagem Go para map e o blog do Go para map, para aprofundares o map.

Sintaticamente, o map tem este aspeto:

map[KeyType]ElementType

O KeyType tem de ser um tipo comparável, enquanto o ElementType pode ser qualquer tipo válido em Go, o que significa que podes guardar desde uma variável primitiva até um slice.

É importante lembrar-te de que o map em Go não tem ordem definida: se tentares percorrer um map com um ciclo e imprimir os elementos, podes surpreender-te ao ver que aparecem por ordem aleatória (experimenta, se quiseres).

Também é importante saberes que cada chave é única, ou seja, atribuir a mesma chave duas vezes substitui o valor associado a essa chave.

O map é um tipo de referência, o que significa que, se o passares adiante, o Go não copia o map todo. Em vez disso, o Go copia o ponteiro do map, o que torna barato passar um map para uma função ou variável. O valor de um map não inicializado é nil.

Podes definir um map assim (também lhe chamamos map nil):

  var foo map[string]int

Para inicializar um map, podes fazer:

  // With map literal
  foo := map[string]int{}

ou

  // or with make function
  foo := make(map[string]int)

Um map nil é diferente de um map inicializado; escrever num map nil provoca um erro em tempo de execução

panic: assignment to entry in nil map

Por isso, é importante inicializar um map antes de o usares

Aqui ficam algumas operações que podes fazer com um map

  // Add a value in a map with the `=` operator:
  foo["bar"] = 42
  // Here we update the element of `bar`
  foo["bar"] = 73
  // To retrieve a map value, you can use
  baz := foo["bar"]
  // To delete an item from a map, you can use
  delete(foo, "bar")

Se tentares obter o valor de uma chave que não existe no map, ele devolve o valor zero do tipo do valor. Isto pode confundir-te, sobretudo se o valor predefinido do teu ElementType (por exemplo, 0 para um int) for um valor válido. Para verificar se uma chave existe no teu map, podes usar

  value, exists := foo["baz"]
  // If the key "baz" does not exist,
  // value: 0; exists: false

Como já vimos, é fácil detetar se um map está inicializado ou não: basta compará-lo com nil. Mas e os outros maps? Bem, como o map não é um tipo comparável, não podemos usar o operador ==, mas o pacote reflect tem algo que nos pode valer, chamado DeepEqual, que podemos usar assim

import "reflect"

// ....

equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true

Mas espera: se o map não é um tipo comparável, porque é que conseguimos compará-lo com nil? Bem, a especificação abriu uma exceção para isto, vê a especificação dos tipos comparáveis

Por último, se tentares escrever num map a partir de várias goroutines, isso vai ativar o race detector; vê a documentação do Go e o blog do Go. Em alternativa, podes usar sync.Map, atomic ou mutex para contornar este problema.

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Mapas

A prática está bloqueada

Desbloqueia mais 3 exercícios para praticares Mapas