Dev Skills

Maps

A hash table with two behaviours that surprise most people once.

Topic 12 of 98 · 5 min to read · 45 min to build
After this topic you can
  • Write code that tells a missing key from a zero value with v, ok := m[k].
  • Explain why ranging over a map may give a different order each time.
  • Fix the panic caused by writing to a nil map.

What is a map in Go?

A map is a hash table: it stores values by key. Keys must be comparable with ==. Strings, numbers, pointers, and structs whose fields are all comparable work. Slices, maps and funcs do not.

maps.go
m := map[string]int{"a": 1}
m["b"] = 2
v := m["missing"] // 0 — a missing key returns the ZERO VALUE, no error
v, ok := m["missing"] // v=0, ok=false — always use this two-value form
delete(m, "a")
fmt.Println(len(m))
for k, v := range m { // ORDER IS RANDOM, on purpose
fmt.Println(k, v)
}
Run it a few times: the plain `range` loop can change order, the sorted one never does.

Three map rules worth memorising now

  • Reading a missing key is not an error. It returns the zero value. Use v, ok := m[k] whenever "absent" and "zero" mean different things.
  • Iteration order is randomised on purpose, so you cannot come to depend on it. To print a map in order, collect the keys into a slice, sort them, then range over the slice.
  • Writing to a `nil` map panics. Reading from one is fine and gives zero values. A map field in a struct is nil until you make it.

Since Go 1.24, maps are built as Swiss tables, a newer hash-table design that makes lookups and inserts faster. Nothing you write changes: the syntax, the rules above and the random order all stay the same. Your old code just runs a bit quicker.

The modern stdlib helpers

Go 1.21 added the slices and maps packages to the standard library. Reach for these before writing a loop.

Preview: maps.Keys does not hand you a slice. It returns an iterator, something that gives you the keys one at a time. You will learn how they work in Iterators; for now, treat slices.Sorted(maps.Keys(m)) as "the keys, sorted".

stdlib.go
s := []int{3, 1, 2}
slices.Contains(s, 3) // true
slices.Sort(s) // s is now [1 2 3]
slices.Index(s, 3) // 2
slices.Reverse(s) // s is now [3 2 1]
m := map[string]int{"b": 2, "a": 1}
keys := slices.Sorted(maps.Keys(m)) // [a b] — preview, explained in Iterators
The short version
  • A missing key returns the zero value, not an error. Use v, ok := m[k].
  • Iteration order is randomised on purpose. Sort the keys when order matters.
  • Writing to a nil map panics; reading from one is fine.
  • Maps are not safe for concurrent use. When the runtime detects a concurrent write, it crashes the program on purpose.
Remember: Absent and zero are different things. The two-value form is how you tell them apart.
Build this before moving on
Count word frequency with a map[string]int, then print the top three. A map cannot be sorted, so you will have to move the pairs into a slice.
Coding challenge · checked by tests
Write WordCount(words []string) map[string]int that counts how many times each word appears. Example: WordCount([]string{"go", "is", "go"}) → map[go:2 is:1]. An empty list gives an empty map.

Tab indents. Press Esc, then Tab, to leave the editor.

Then try these in your own editor
  1. 1Read a missing key and print the result. Then use the two-value form.
  2. 2Write to a map declared but never made, and read the panic.
  3. 3Range over the same map five times and watch the order change.
Check yourselfTake the Core language test