Mappe in Go
Le mappe (map) in Go sono strutture dati che associano chiavi a valori, simili ai dizionari in Python o agli HashMap in Java. Sono tipi riferimento, altamente efficienti e integrati nel linguaggio. In questa guida vedremo come creare, utilizzare e gestire le mappe in Go.
Creare una Mappa con make
Il modo piu comune per creare una mappa e usare la funzione make.
package main
import "fmt"
func main() {
// Crea una mappa con chiavi string e valori int
eta := make(map[string]int)
// Aggiungere elementi
eta["Alice"] = 30
eta["Marco"] = 25
eta["Giulia"] = 28
fmt.Println(eta) // map[Alice:30 Giulia:28 Marco:25]
}
Creare una Mappa con Letterali
Le mappe possono essere inizializzate direttamente con un letterale.
capitali := map[string]string{
"Italia": "Roma",
"Francia": "Parigi",
"Germania": "Berlino",
"Spagna": "Madrid",
}
fmt.Println(capitali)
Nota: l’ultima voce deve avere la virgola finale se la parentesi graffa di chiusura e su una riga separata.
Accesso agli Elementi
Si accede ai valori di una mappa usando la chiave tra parentesi quadre.
capitali := map[string]string{
"Italia": "Roma",
"Francia": "Parigi",
}
fmt.Println(capitali["Italia"]) // Roma
fmt.Println(capitali["Francia"]) // Parigi
// Chiave inesistente: restituisce il valore zero del tipo
fmt.Println(capitali["Giappone"]) // "" (stringa vuota)
Quando si accede a una chiave inesistente, Go restituisce il valore zero del tipo del valore, senza errore.
Verificare l’Esistenza: Il Pattern Comma Ok
Per distinguere tra un valore zero e una chiave inesistente, si usa il pattern “comma ok”.
package main
import "fmt"
func main() {
punteggi := map[string]int{
"Alice": 95,
"Marco": 0, // Punteggio zero, ma la chiave esiste
}
// Pattern comma ok
punteggio, esiste := punteggi["Alice"]
if esiste {
fmt.Println("Punteggio di Alice:", punteggio) // 95
}
punteggio, esiste = punteggi["Marco"]
if esiste {
fmt.Println("Punteggio di Marco:", punteggio) // 0
}
punteggio, esiste = punteggi["Giulia"]
if !esiste {
fmt.Println("Giulia non trovata nella mappa")
}
}
Il secondo valore restituito e un booleano che indica se la chiave esiste nella mappa.
Eliminare Elementi
La funzione delete rimuove un elemento dalla mappa.
colori := map[string]string{
"rosso": "#FF0000",
"verde": "#00FF00",
"blu": "#0000FF",
}
fmt.Println(len(colori)) // 3
delete(colori, "verde")
fmt.Println(len(colori)) // 2
fmt.Println(colori) // map[blu:#0000FF rosso:#FF0000]
// Eliminare una chiave inesistente non causa errori
delete(colori, "giallo") // Nessun effetto
Iterare sulle Mappe
Si usa for range per iterare su chiavi e valori di una mappa.
popolazione := map[string]int{
"Roma": 2873000,
"Milano": 1396000,
"Napoli": 967000,
"Torino": 870000,
}
for citta, abitanti := range popolazione {
fmt.Printf("%s: %d abitanti\n", citta, abitanti)
}
Importante: l’ordine di iterazione sulle mappe non e garantito. Se si necessita di un ordine specifico, bisogna ordinare le chiavi separatamente.
import "sort"
// Iterazione ordinata per chiave
chiavi := make([]string, 0, len(popolazione))
for k := range popolazione {
chiavi = append(chiavi, k)
}
sort.Strings(chiavi)
for _, k := range chiavi {
fmt.Printf("%s: %d\n", k, popolazione[k])
}
Mappe come Tipi Riferimento
Le mappe sono tipi riferimento. Quando si assegna una mappa a un’altra variabile o la si passa a una funzione, entrambe puntano alla stessa struttura dati sottostante.
package main
import "fmt"
func aggiungiElemento(m map[string]int, chiave string, valore int) {
m[chiave] = valore
}
func main() {
dati := map[string]int{"a": 1, "b": 2}
aggiungiElemento(dati, "c", 3)
fmt.Println(dati) // map[a:1 b:2 c:3]
}
La funzione modifica la mappa originale perche riceve un riferimento, non una copia.
Mappa Nil vs Mappa Vuota
Una mappa nil e una mappa vuota hanno comportamenti diversi.
var nilMap map[string]int // Mappa nil
vuota := map[string]int{} // Mappa vuota
vuotaMake := make(map[string]int) // Mappa vuota
fmt.Println(nilMap == nil) // true
fmt.Println(vuota == nil) // false
fmt.Println(vuotaMake == nil) // false
// Leggere da una mappa nil e sicuro
fmt.Println(nilMap["chiave"]) // 0
// ATTENZIONE: scrivere su una mappa nil causa un panic!
// nilMap["chiave"] = 1 // panic: assignment to entry in nil map
E fondamentale inizializzare sempre una mappa prima di scriverci.
Mappe con Valori Complessi
I valori di una mappa possono essere di qualsiasi tipo, incluse slice, struct e altre mappe.
// Mappa di slice
studentiPerCorso := map[string][]string{
"matematica": {"Alice", "Marco", "Giulia"},
"fisica": {"Marco", "Laura"},
"informatica": {"Alice", "Laura", "Paolo"},
}
for corso, studenti := range studentiPerCorso {
fmt.Printf("%s: %v\n", corso, studenti)
}
// Mappa di mappe
organigramma := map[string]map[string]string{
"sviluppo": {
"responsabile": "Alice",
"senior": "Marco",
},
"marketing": {
"responsabile": "Giulia",
},
}
fmt.Println(organigramma["sviluppo"]["responsabile"]) // Alice
Conteggio delle Occorrenze
Un pattern molto comune con le mappe e il conteggio delle occorrenze.
testo := "il gatto e il cane e il topo"
parole := strings.Fields(testo)
conteggio := make(map[string]int)
for _, parola := range parole {
conteggio[parola]++
}
for parola, n := range conteggio {
fmt.Printf("%s: %d\n", parola, n)
}
Usare le Mappe come Insiemi (Set)
Go non ha un tipo set nativo. Le mappe con valori booleani o struct{} sono il sostituto idiomatico.
// Set con valori bool
visitati := map[string]bool{}
visitati["Roma"] = true
visitati["Milano"] = true
if visitati["Roma"] {
fmt.Println("Roma e stata visitata")
}
// Set con struct{} (piu efficiente in memoria)
unici := map[int]struct{}{}
numeri := []int{1, 2, 3, 2, 1, 4, 3, 5}
for _, n := range numeri {
unici[n] = struct{}{}
}
fmt.Println("Elementi unici:", len(unici)) // 5
Conclusione
Le mappe in Go sono una struttura dati fondamentale e versatile. Il pattern comma ok per verificare l’esistenza delle chiavi, il comportamento come tipo riferimento e la distinzione tra mappe nil e vuote sono concetti essenziali da padroneggiare. Ricorda che l’ordine di iterazione non e garantito e che scrivere su una mappa nil causa un panic. Le mappe possono essere usate anche come sostituto degli insiemi, un tipo non presente nativamente in Go.