Introduction
Maps are Go's built-in hash table implementation, providing fast key-value storage and lookup. They're essential for caches, indexes, configurations, and any scenario requiring associative access. Understanding map semantics—especially nil maps and concurrent access—prevents common runtime panics.
Key Concepts
Hash Table: Data structure providing O(1) average-case lookup, insertion, and deletion.
Reference Type: Maps are pointers to underlying data; passing to functions shares the map.
Comma-Ok Idiom: Pattern v, ok := m[key] distinguishing missing keys from zero values.
Not Concurrency-Safe: Maps require external synchronization for concurrent read/write access.
Real World Context
Maps power caches, session stores, configuration lookup, counting/grouping, and graph adjacency lists. The random iteration order prevents code from accidentally depending on insertion order (a common source of test flakiness). The nil map panic catches uninitialized map bugs at development time.
Deep Dive
Creating Maps
go// Using make m := make(map[string]int) // With initial capacity (optimization) m := make(map[string]int, 100) // Map literal m := map[string]int{ "one": 1, "two": 2, } // Nil map (read-only, writes panic!) var m map[string]int // nil
Basic Operations
gom := make(map[string]int) // Insert/Update m["key"] = 42 // Read (returns zero value if missing) val := m["key"] // Delete delete(m, "key") // Length fmt.Println(len(m))
Comma-Ok Idiom
gom := map[string]int{"a": 0} // Without ok: Can't distinguish missing from zero val := m["a"] // 0 val := m["b"] // 0 (missing, but same!) // With ok: Proper detection val, ok := m["a"] // val=0, ok=true val, ok := m["b"] // val=0, ok=false if v, ok := m["key"]; ok { fmt.Println("Found:", v) } else { fmt.Println("Not found") }
Iteration
gofor key, value := range m { fmt.Println(key, value) } // Order is RANDOM - changes each run!
Maps with Struct Keys
gotype Point struct{ X, Y int } m := map[Point]string{ {0, 0}: "origin", {1, 0}: "right", }
Common Pitfalls
-
Writing to nil map panics:
var m map[string]int; m["x"] = 1crashes. Always initialize withmakeor a literal. -
Assuming iteration order: Map iteration is intentionally randomized. Don't rely on any ordering.
-
Concurrent map writes: Concurrent reads are safe, but concurrent read+write causes runtime panic. Use
sync.Mapor mutex.
Best Practices
- Always initialize maps before writing:
m := make(map[K]V). - Use the comma-ok idiom when zero values are valid.
- Use
sync.Maporsync.RWMutexfor concurrent access. - Pre-size with
make(map[K]V, n)when size is known.
Summary
Maps provide O(1) hash table operations with m[key] syntax. They're reference types that must be initialized before writing. The comma-ok idiom (v, ok := m[key]) distinguishes missing keys from zero values. Iteration order is randomized, and concurrent writes require synchronization.
Code Examples
func main() {
m := map[string]string{"foo": "bar", "baz": "qux"}
if v, ok := m["foo"]; ok {
fmt.Println("Found foo:", v)
}
}