En Go, map es un tipo de dato incorporado que representa una tabla hash.
En otros lenguajes de programación, puede que conozcas el map como dict, como array asociativo o como almacén de clave/valor.
Si no estás familiarizado con este concepto, puedes pensar en el map como en un diccionario, en el que cada palabra es la key y la definición es el element del map.
Antes de empezar, me gustaría remitirte a la especificación de Go para los map y al blog de Go sobre los map para profundizar más en
map.
Sintácticamente, map tiene este aspecto:
map[KeyType]ElementType
KeyType debe ser cualquier tipo comparable, mientras que ElementType puede ser cualquier tipo válido en Go, lo que significa que puedes almacenar desde una variable primitiva hasta un slice.
Es importante recordar que map en Go no está ordenado; si intentas recorrer un map con un bucle e imprimir los elementos, puede que te sorprendas al ver que se imprimen en un orden aleatorio (pruébalo si quieres).
También es importante saber que cada clave es única, lo que significa que asignar la misma clave dos veces sobrescribirá el valor de esa clave.
map es un tipo por referencia, lo que significa que si lo pasas de un sitio a otro, Go no copiará todo el map.
En su lugar, lo que hace Go es copiar el puntero del map, lo que hace que pasar un map a una función o a una variable sea barato.
El valor de un map sin inicializar es nil.
Puedes definir un map de la siguiente manera (a esto también lo llamamos un nil map);
var foo map[string]int
Para inicializar un map, puedes hacer lo siguiente:
// With map literal
foo := map[string]int{}
o
// or with make function
foo := make(map[string]int)
Un map nil es distinto de un map inicializado; escribir en un map nil provocará un error en tiempo de ejecución
panic: assignment to entry in nil mapPor lo tanto, es importante inicializar un map antes de usarlo
Estas son algunas operaciones que puedes hacer con un 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")
Si intentas obtener el valor de una clave que no existe en el map, se devolverá el valor cero del tipo del valor.
Esto puede confundirte, sobre todo si el valor predeterminado de tu ElementType (por ejemplo, 0 para un int) es un valor válido.
Para comprobar si una clave existe en tu map, puedes usar
value, exists := foo["baz"]
// If the key "baz" does not exist,
// value: 0; exists: false
Como hemos visto antes, detectar si un map está inicializado o no es fácil, basta con compararlo con nil, pero ¿qué pasa con otros maps? Bueno, como map no es comparable, no podemos usar el operador ==, pero el paquete reflect tiene algo que nos puede rescatar: se llama DeepEqual y podemos usarlo así
import "reflect"
// ....
equal := reflect.DeepEqual(map[string]int{}, map[string]int{})
fmt.Println(equal)
// Output: true
Pero espera, si map no es comparable, ¿por qué podemos compararlo con nil? Bueno, la especificación ha hecho una excepción para esto, consulta la especificación de tipos comparables
Por último, si intentas escribir en un map desde varias goroutines, se activará el detector de carreras; consulta la documentación de Go y el blog de Go.
Como alternativa, puedes usar sync.Map, atomic o mutex para sortear este problema.