Maps
## Objetivos de Aprendizaje
- Comprender los maps de Go (tablas hash)
- Crear e inicializar maps
- Añadir, acceder y eliminar entradas
- Iterar sobre maps
- Manejar claves faltantes de forma segura
## ¿Qué es un Map?
Un map es una implementación de tabla hash - una colección no ordenada de pares clave-valor donde las claves son únicas.
## Declaración
### Con make
```go
ages := make(map[string]int)
ages["Alice"] = 30
ages["Bob"] = 25
```
### Con Literales
```go
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Carol": 35,
}
```
### Map Nil
```go
var ages map[string]int // map nil (no se puede añadir a él)
ages = make(map[string]int) // Inicializar antes de usar
```
## Operaciones Básicas
### Añadir/Actualizar
```go
ages := make(map[string]int)
ages["Alice"] = 30 // Añadir
ages["Alice"] = 31 // Actualizar
```
### Acceder
```go
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
fmt.Println(ages["Alice"]) // 30
fmt.Println(ages["Unknown"]) // 0 (valor cero)
```
### Verificar Existencia de Clave
```go
ages := map[string]int{
"Alice": 30,
}
value, exists := ages["Alice"]
fmt.Println(value, exists) // 30 true
value, exists = ages["Bob"]
fmt.Println(value, exists) // 0 false
```
### Eliminar
```go
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
delete(ages, "Bob")
fmt.Println(ages) // map[Alice:30]
```
### Eliminar Clave No Existente
```go
ages := map[string]int{"Alice": 30}
delete(ages, "Bob") // Seguro - sin error aunque la clave no exista
```
## Longitud
```go
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
fmt.Println(len(ages)) // 2
```
## Iteración
### Iteración Básica
```go
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Carol": 35,
}
for key, value := range ages {
fmt.Printf("%s: %d\n", key, value)
}
```
### Orden
- El orden de iteración de un map **no está garantizado**
- El orden puede diferir entre iteraciones
### Solo Clave
```go
for key := range ages {
fmt.Println(key)
}
```
### Claves Ordenadas
```go
ages := map[string]int{
"Charlie": 35,
"Alice": 30,
"Bob": 25,
}
keys := make([]string, 0, len(ages))
for key := range ages {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("%s: %d\n", key, ages[key])
}
```
## Valor Cero
### Leer de un Map Nil
```go
var ages map[string]int // nil
fmt.Println(ages["Alice"]) // 0
fmt.Println(len(ages)) // 0
delete(ages, "Alice") // Seguro
// ages["Alice"] = 30 // ¡PANIC!
```
### Verificar Antes de Escribir
```go
var ages map[string]int
if ages == nil {
ages = make(map[string]int)
}
ages["Alice"] = 30 // Ahora es seguro
```
## Map como Referencia
Los maps son tipos de referencia - copiar un map comparte los datos subyacentes.
```go
ages1 := map[string]int{"Alice": 30}
ages2 := ages1
ages2["Alice"] = 31
fmt.Println(ages1["Alice"]) // 31 (ambos referencian el mismo map)
fmt.Println(ages2["Alice"]) // 31
```
## Punteros a Maps
```go
func modify(m map[string]int) {
m["Alice"] = 31
}
ages := map[string]int{"Alice": 30}
modify(ages)
fmt.Println(ages["Alice"]) // 31
```
## Patrones Comunes
### Conteo de Palabras
```go
text := "hello world hello go programming hello"
words := strings.Fields(text)
count := make(map[string]int)
for _, word := range words {
count[word]++
}
fmt.Println(count) // map[hello:3 world:1 go:1 programming:1]
```
### Implementación de Set
```go
type Set struct {
items map[string]struct{}
}
func NewSet() *Set {
return &Set{make(map[string]struct{})}
}
func (s *Set) Add(item string) {
s.items[item] = struct{}{}
}
func (s *Set) Contains(item string) bool {
_, exists := s.items[item]
return exists
}
func (s *Set) Remove(item string) {
delete(s.items, item)
}
func main() {
set := NewSet()
set.Add("apple")
set.Add("banana")
fmt.Println(set.Contains("apple")) // true
fmt.Println(set.Contains("orange")) // false
}
```
### Agrupar Por
```go
people := []struct {
Name string
Age int
}{
{"Alice", 30},
{"Bob", 25},
{"Carol", 30},
{"David", 25},
}
groups := make(map[int][]string)
for _, p := range people {
groups[p.Age] = append(groups[p.Age], p.Name)
}
fmt.Println(groups)
// map[25:[Bob David] 30:[Alice Carol]]
```
### Valores Únicos
```go
func unique(ints []int) []int {
seen := make(map[int]bool)
result := []int{}
for _, n := range ints {
if !seen[n] {
seen[n] = true
result = append(result, n)
}
}
return result
}
```
## Comparación
### Los Maps No Pueden Ser Comparados
```go
m1 := map[string]int{"a": 1}
m2 := map[string]int{"a": 1}
// m1 == m2 // ERROR DE COMPILACIÓN: map solo puede compararse con nil
```
### Comparación Profunda
```go
func equalMaps(m1, m2 map[string]int) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || v1 != v2 {
return false
}
}
return true
}
```
## Acceso Concurrente
### Condición de Carrera
Los maps no son seguros para acceso concurrente por defecto.
```go
var counter = make(map[string]int)
// ¡Esto es inseguro!
go func() {
for i := 0; i < 1000; i++ {
counter["a"]++
}
}()
go func() {
for i := 0; i < 1000; i++ {
counter["a"]++
}
}()
```
### sync.RWMutex
```go
import "sync"
var counter = struct {
sync.RWMutex
m map[string]int
}{m: make(map[string]int)}
counter.Lock()
counter.m["a"]++
counter.Unlock()
counter.RLock()
fmt.Println(counter.m["a"])
counter.RUnlock()
```
### sync.Map
```go
var syncMap sync.Map
syncMap.Store("a", 1)
value, ok := syncMap.Load("a")
syncMap.Delete("a")
syncMap.Range(func(key, value interface{}) bool {
fmt.Printf("%s: %d\n", key, value)
return true
})
```
## Resumen
- Los maps son tablas hash - pares clave-valor con claves únicas
- Crear con `make()` o literales de map
- El valor cero es `nil` - no se puede añadir a un map nil
- Leer clave no existente retorna valor cero
- Usar el idiom comma-ok para verificar existencia de clave
- `delete()` es seguro incluso si la clave no existe
- El orden de iteración no está garantizado
- Los maps son tipos de referencia
- No son seguros para acceso concurrente - usar sync.RWMutex o sync.Map
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →