00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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.