Introduction
Maps are Go’s built-in associative data type (sometimes called hashes or dicts in other languages). They store key-value pairs and provide fast lookups, inserts, and deletes based on keys.
The Code
// _Maps_ are Go's built-in [associative data type](https://en.wikipedia.org/wiki/Associative_array)
// (sometimes called _hashes_ or _dicts_ in other languages).
package main
import (
"fmt"
"maps"
)
func main() {
// To create an empty map, use the builtin `make`:
// `make(map[key-type]val-type)`.
m := make(map[string]int)
// Set key/value pairs using typical `name[key] = val`
// syntax.
m["k1"] = 7
m["k2"] = 13
// Printing a map with e.g. `fmt.Println` will show all of
// its key/value pairs.
fmt.Println("map:", m)
// Get a value for a key with `name[key]`.
v1 := m["k1"]
fmt.Println("v1:", v1)
// If the key doesn't exist, the
// [zero value](https://go.dev/ref/spec#The_zero_value) of the
// value type is returned.
v3 := m["k3"]
fmt.Println("v3:", v3)
// The builtin `len` returns the number of key/value
// pairs when called on a map.
fmt.Println("len:", len(m))
// The builtin `delete` removes key/value pairs from
// a map.
delete(m, "k2")
fmt.Println("map:", m)
// To remove *all* key/value pairs from a map, use
// the `clear` builtin.
clear(m)
fmt.Println("map:", m)
// The optional second return value when getting a
// value from a map indicates if the key was present
// in the map. This can be used to disambiguate
// between missing keys and keys with zero values
// like `0` or `""`. Here we didn't need the value
// itself, so we ignored it with the _blank identifier_
// `_`.
_, prs := m["k2"]
fmt.Println("prs:", prs)
// You can also declare and initialize a new map in
// the same line with this syntax.
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
// The `maps` package contains a number of useful
// utility functions for maps.
n2 := map[string]int{"foo": 1, "bar": 2}
if maps.Equal(n, n2) {
fmt.Println("n == n2")
}
}
Map Fundamentals
Creating Maps
m := make(map[string]int)
Use make to create an empty map. The syntax is make(map[key-type]value-type). This creates a map with string keys and int values.
An uninitialized map (var m map[string]int) is nil and will panic if you try to write to it. Always initialize maps with make or a map literal.
Setting Values
Set key/value pairs using square bracket notation. If the key already exists, its value is updated.
Getting Values
v1 := m["k1"]
fmt.Println("v1:", v1) // v1: 7
Retrieve values using the key. If the key doesn’t exist, you get the zero value for the value type:
v3 := m["k3"] // k3 doesn't exist
fmt.Println("v3:", v3) // v3: 0 (zero value for int)
Map Operations
Checking Key Existence
_, prs := m["k2"]
fmt.Println("prs:", prs) // false if key doesn't exist
The “comma ok” idiom uses a second return value to check if a key exists:
- First value: the value (or zero value if key doesn’t exist)
- Second value:
true if key exists, false otherwise
Use the comma ok idiom to distinguish between a missing key and a key with a zero value: value, exists := m[key]
Map Length
fmt.Println("len:", len(m)) // Number of key/value pairs
The len function returns the number of key/value pairs in the map.
Deleting Keys
The delete builtin removes a key/value pair from a map. It’s safe to delete a key that doesn’t exist (no-op).
Clearing All Keys
The clear builtin (Go 1.21+) removes all key/value pairs from a map, leaving it empty.
Map Initialization
n := map[string]int{"foo": 1, "bar": 2}
Declare and initialize a map in one line using a map literal. This is more concise than make when you know the initial values.
// Empty map literal
m := map[string]int{}
// With initial values
m := map[string]int{
"alice": 25,
"bob": 30,
"carol": 28,
}
The Maps Package
import "maps"
n2 := map[string]int{"foo": 1, "bar": 2}
if maps.Equal(n, n2) {
fmt.Println("n == n2")
}
The maps package (Go 1.21+) provides useful utilities:
maps.Equal(): Compare two maps
maps.Clone(): Create a shallow copy
maps.Copy(): Copy entries from one map to another
- And more…
You cannot compare maps directly with == (except to nil). Use maps.Equal() from the maps package.
Map Characteristics
Unordered
Maps are unordered - iteration order is not guaranteed and may vary between runs.
Reference Type
Maps are reference types. When you pass a map to a function, both the function and caller refer to the same underlying data.
func modify(m map[string]int) {
m["key"] = 100 // Modifies the original map
}
Valid Key Types
Key types must be comparable (support == and !=):
- ✅ Strings, integers, floats, booleans, pointers, channels, arrays
- ❌ Slices, maps, functions
Common Patterns
Checking and Setting
if _, exists := m[key]; !exists {
m[key] = defaultValue
}
Iterating Over Maps
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
Map of Slices
m := make(map[string][]int)
m["key"] = append(m["key"], 1, 2, 3)
Counting Occurrences
counts := make(map[string]int)
for _, item := range items {
counts[item]++ // Zero value (0) works perfectly
}
Zero Value Behavior
Go’s zero values make maps very convenient. For numeric values, you can increment without checking if the key exists: m[key]++ works even if key is new.
Key Takeaways
- Create maps with
make(map[K]V) or map literals map[K]V{}
- Access values with
m[key], set with m[key] = value
- Use comma ok idiom
value, exists := m[key] to check existence
- Missing keys return the zero value for the value type
- Delete keys with
delete(m, key)
- Clear all entries with
clear(m) (Go 1.21+)
- Maps are unordered - don’t rely on iteration order
- Maps are reference types - passed by reference to functions
- Use the
maps package for utilities like Equal() and Clone()
- Only comparable types can be map keys (no slices, maps, or functions)