Skip to content

Beginner · Collections and structs · topic 14 of 46 · 35 min

Maps: key-value look-ups

Explanation

A map stores values by key, like a phone book stores numbers by name. You read with m[key], add with m[key] = value and remove with delete. A missing key gives the zero value, so use the "comma ok" form to know if it was really there. Iteration order is random, writing to a nil map panics, and maps are not safe for writes from several goroutines at once.

Why maps?

With a slice you find items by position. Often you want to find them by a name instead: the price of "chai", the marks of roll number 23, the city for PIN code 411001. A map is a collection of key-value pairs where you look up a value by its key, very fast, even with millions of entries.

Think of it like this: A map is like the dabba-wala's code system in Mumbai: the code painted on each tiffin (the key) tells exactly which office desk (the value) it goes to. Nobody searches every desk.

Creating and using a map

go
prices := map[string]int{
	"chai":   15,
	"samosa": 20,
}
prices["vada pav"] = 25        // add
prices["chai"] = 18            // update
delete(prices, "samosa")       // remove
fmt.Println(prices["chai"], len(prices)) // 18 2
fmt.Println(prices)            // map[chai:18 vada pav:25]
The type is map[KeyType]ValueType. A trailing comma is needed after the last line of a multi-line literal.
go
counts := make(map[string]int)  // an empty map, ready to use
make creates an empty map. map[string]int{} does the same.

Missing keys and "comma ok"

Reading a key that is not in the map does not crash. It gives the zero value of the value type: 0, "", false. That is handy for counting, but it means you cannot tell "price is 0" from "item not on the menu". The comma ok form answers that.

go
price, ok := prices["dosa"]
if !ok {
	fmt.Println("dosa is not on the menu")
} else {
	fmt.Println("dosa costs", price)
}

if p, ok := prices["chai"]; ok {
	fmt.Println("chai:", p)
}
ok is true only when the key exists.

Counting with a map

go
words := strings.Fields("chai samosa chai chai vada")
count := map[string]int{}
for _, w := range words {
	count[w]++   // missing keys start at 0
}
fmt.Println(count) // map[chai:3 samosa:1 vada:1]
fmt prints maps with keys sorted, which makes output easy to read and test.

Iteration order is random

for k, v := range m visits every pair, but the order is not specified and Go deliberately changes it between runs, so nobody relies on it. When you need a stable order (for a report or a test), collect the keys, sort them, then loop over the sorted keys.

go
keys := make([]string, 0, len(count))
for k := range count {
	keys = append(keys, k)
}
slices.Sort(keys)
for _, k := range keys {
	fmt.Println(k, count[k])
}
// Go 1.23+: keys := slices.Sorted(maps.Keys(count))
Sorted keys give the same output every run.

Three rules that cause crashes

  • A nil map can be read but not written. var m map[string]int; m["a"] = 1 panics with "assignment to entry in nil map". Always create maps with make or a literal.
  • Maps are not safe for concurrent use. If one goroutine writes while another reads or writes, Go may stop the program with "fatal error: concurrent map writes", which cannot be recovered. Protect the map with a sync.Mutex (module 6) or use sync.Map for special cases.
  • Keys must be comparable with ==. Strings, numbers, bools, arrays and structs of those work; slices, maps and functions cannot be keys.

Maps share data

Like slices, a map value is a small reference to the real data. Passing a map to a function and changing it inside changes the caller's map too. Copy it (maps.Clone in Go 1.21+) if you need a separate version.

Maps of slices and of structs

go
byCity := map[string][]string{}
byCity["Pune"] = append(byCity["Pune"], "Asha")  // nil slice + append works
byCity["Pune"] = append(byCity["Pune"], "Ravi")
fmt.Println(byCity) // map[Pune:[Asha Ravi]]

type Stock struct{ Qty int }
stock := map[string]Stock{"rice": {Qty: 10}}
// stock["rice"].Qty--    // compile error: cannot assign to struct field in map
s := stock["rice"]
s.Qty--
stock["rice"] = s          // read, change, write back
Map elements are not addressable. For frequent updates, store pointers: map[string]*Stock.
Tip: Go 1.21 added the built-in clear(m), which deletes every entry, and the maps package with maps.Clone, maps.Copy and maps.Equal.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Maps: key-value look-ups · 1/7

Maps

A map looks up a value by its key, like a phone book or a price list.

Prefer a video by a teacher? Find free videos on “golang maps tutorial comma ok idiom iteration order” on YouTube ↗

In real life: PIN code to city look-up

A delivery app checks if it serves a customer's PIN code before showing the checkout button. A map from PIN code to city answers instantly. The comma ok form separates "not served" from a real entry, and the app shows "Sorry, we do not deliver here yet" for unknown codes.

go
package main

import "fmt"

func main() {
	served := map[string]string{
		"411001": "Pune",
		"560001": "Bengaluru",
		"110001": "New Delhi",
	}
	for _, pin := range []string{"560001", "999999"} {
		if city, ok := served[pin]; ok {
			fmt.Println(pin, "→ we deliver in", city)
		} else {
			fmt.Println(pin, "→ sorry, not served yet")
		}
	}
}
Prints 560001 → we deliver in Bengaluru, then 999999 → sorry, not served yet.

Practice (0/4 done)

Task 1 · Question

What does this print? m := map[string]int{"a": 1} fmt.Println(m["b"])

Task 2 · Question

What does this print? m := map[string]int{"b": 2, "a": 1} fmt.Println(m)

Task 3 · Question

Which line panics? 1: var m map[string]int 2: fmt.Println(m["x"]) 3: m["x"] = 1

Task 4 · Practice

Given a slice of UPI transactions as category strings (for example "food", "travel", "food", "bills"), count how many times each category appears and print the categories in alphabetical order with their counts.

Quick quiz

Question 1

1.What does v, ok := m[k] give when k is missing?

Question 2

2.Which cannot be a map key?

Question 3

3.Is the order of for k := range m the same every run?

Question 4

4.Two goroutines write to the same map without a lock. What can happen?

Question 5

5.How do you remove key "a" from map m?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does v, ok := m[k] give when k is missing?

Answer: zero value and false

ok is false and v is the zero value of the value type.

Which cannot be a map key?

Answer: []int

Slices are not comparable, so they cannot be keys.

Is the order of for k := range m the same every run?

Answer: No, it is unspecified and randomised

Never rely on map iteration order.

Two goroutines write to the same map without a lock. What can happen?

Answer: Go may crash with "fatal error: concurrent map writes"

Unprotected concurrent map writes are a fatal run-time error. Use a mutex.

How do you remove key "a" from map m?

Answer: delete(m, "a")

The built-in delete function removes a key. Deleting a missing key does nothing.

How does Go provide polymorphism without inheritance?Related

Answer: Interfaces satisfied implicitly

Any type with the right methods can be used through an interface.

From Go language interview questions

What is the zero value of a pointer?Related

Answer: nil

An unset pointer is nil.

From Pointers without fear

What is the time to find a key in std::map with n keys?Related

Answer: O(log n)

std::map is a balanced tree, so its height is about log n.

From std::map and std::unordered_map

Which three things make up a slice header?Related

Answer: Pointer to array, length, capacity

A slice is a pointer, a length and a capacity.

From Slices in depth: length, capacity and sharing

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Collections and structs” before its exam: Structs and methods.
  2. Module 3 exam: Collections and structs · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Pointers, interfaces and errors. 46 topics left in the course before the final exam.

← Previous: Strings, bytes and runes