Salta al contenuto
Go

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.

Contenuto redatto con l'assistenza di strumenti di intelligenza artificiale e rivisto dalla redazione di Codegrind, che ne è responsabile. Hai trovato un errore? Scrivici.