LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1817 min read

Maps

Learn how to declare, read, write, and delete from Go maps, use the comma-ok idiom to check key existence, and why iteration order isn't guaranteed.

Introduction

A map is Go's built-in key-value collection type, similar to a dictionary in Python, a HashMap in Java, or an object used as a lookup table in JavaScript. Maps give you fast, average O(1) lookups by key, and they're used constantly for things like counting occurrences, caching, and grouping data.

What You Will Learn
  • How to declare and initialize maps.
  • How to read, write, and update values by key.
  • How to safely check whether a key exists using the comma-ok idiom.
  • How to remove a key with delete.
  • Why map iteration order is randomized in Go.

Declaring and Initializing Maps

A map type is written as map[KeyType]ValueType. Maps can be created with a map literal or with the built-in make function. A map declared with var but never initialized is a nil map — you can read from it safely, but writing to it causes a runtime panic.

package main
import "fmt"
func main() {
// Map literal
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
fmt.Println(ages)
// make() creates an empty, ready-to-use map
inventory := make(map[string]int)
inventory["apples"] = 50
fmt.Println(inventory)
// A nil map: reads are safe, writes panic
var nilMap map[string]int
fmt.Println(nilMap["missing"]) // prints 0, the zero value — no panic
}
Output

Click Run to see what this code prints.

Reading and Writing Values

You read and write map values using square-bracket indexing, just like a slice — except the index is a key instead of a position. Reading a key that doesn't exist returns the zero value for the value type, without any error or panic.

package main
import "fmt"
func main() {
stock := map[string]int{"pen": 100, "pencil": 200}
stock["eraser"] = 75 // add a new key
stock["pen"] = stock["pen"] - 10 // update an existing key
fmt.Println("pens left:", stock["pen"])
fmt.Println("staplers:", stock["stapler"]) // key doesn't exist -> 0
fmt.Println("total distinct items:", len(stock))
}
Output

Click Run to see what this code prints.

The Comma-Ok Idiom

Since reading a missing key silently returns the zero value, you can't tell the difference between "the key is missing" and "the key exists but its value happens to be zero" from a single-value read. Go solves this with the comma-ok idiom: indexing a map can return two values — the value, and a boolean indicating whether the key was actually found.

package main
import "fmt"
func main() {
scores := map[string]int{"Alice": 0, "Bob": 88}
value, ok := scores["Alice"]
fmt.Println("Alice:", value, "found:", ok) // found: true, value is genuinely 0
value, ok = scores["Charlie"]
fmt.Println("Charlie:", value, "found:", ok) // found: false
if v, ok := scores["Bob"]; ok {
fmt.Println("Bob's score is", v)
}
}
Output

Click Run to see what this code prints.

Deleting Keys

The built-in delete function removes a key (and its value) from a map. If the key doesn't exist, delete is a harmless no-op — it never panics.

package main
import "fmt"
func main() {
cart := map[string]int{"apple": 3, "banana": 2, "cherry": 10}
delete(cart, "banana")
fmt.Println(cart)
delete(cart, "does-not-exist") // safe, does nothing
fmt.Println(cart)
}
Output

Click Run to see what this code prints.

Iterating Over a Map

You can loop over a map's key-value pairs with range. Crucially, Go deliberately randomizes map iteration order on every run — this is intentional, to prevent code from accidentally depending on an order that was never guaranteed. If you need a predictable order, sort the keys yourself first.

package main
import (
"fmt"
"sort"
)
func main() {
population := map[string]int{
"Tokyo": 37400000,
"Delhi": 30300000,
"Shanghai": 27100000,
}
// Collect and sort keys for deterministic, repeatable output.
keys := make([]string, 0, len(population))
for city := range population {
keys = append(keys, city)
}
sort.Strings(keys)
for _, city := range keys {
fmt.Printf("%s: %d\n", city, population[city])
}
}
Output

Click Run to see what this code prints.

Order Is Never Guaranteed

Do not rely on the order keys appear in when you range over a map directly, even if it happens to look consistent in testing. Go's runtime deliberately randomizes it, and code that depends on it can break unpredictably.

Common Mistakes

Avoid These Mistakes
  • Writing to a nil map (declared with var but never initialized with make or a literal) — this causes a runtime panic.
  • Using a single-value read to check existence, then being confused when a real zero value looks identical to a missing key.
  • Assuming range over a map returns keys in insertion or sorted order — it does not.
  • Modifying a map while ranging over it in ways that add new keys, which has undefined behavior for those new entries.
  • Using a non-comparable type (like a slice) as a map key — this fails to compile.

Best Practices

  • Always initialize maps with make() or a literal before writing to them.
  • Use the comma-ok idiom whenever the zero value is a valid, meaningful value for your data.
  • Sort keys explicitly whenever you need deterministic iteration order for display or testing.
  • Use make(map[K]V, sizeHint) when you know roughly how many entries you'll add, to reduce reallocations.
  • Prefer struct keys or string keys over complex composite keys when simplicity is possible.

Frequently Asked Questions

No. Concurrent reads and writes to the same map from multiple goroutines require external synchronization, such as a sync.Mutex or sync.Map.

Any comparable type — this includes strings, numbers, booleans, and structs made entirely of comparable fields. Slices, maps, and functions cannot be used as keys.

Yes, len(m) returns the number of key-value pairs currently stored in the map.

To prevent developers from writing code that accidentally depends on an ordering the language never promised, which historically caused subtle bugs in other languages when internal hash implementations changed.

Key Takeaways

  • A map type is written map[KeyType]ValueType and stores key-value pairs.
  • Reading a missing key returns the zero value silently — use the comma-ok idiom to detect absence explicitly.
  • delete removes a key safely, even if that key doesn't exist.
  • Map iteration order is randomized by design; sort keys yourself for a predictable order.
  • A nil map can be read from safely but panics on write — always initialize with make() or a literal first.

Summary

Maps round out Go's core collection types alongside arrays and slices, giving you fast key-based lookups for the huge number of problems that involve counting, grouping, or caching. The comma-ok idiom is a pattern you'll see reused throughout Go — including with channels and type assertions later on. Next, you'll dig into how Go represents text: strings and runes.

Next Lesson →

Strings & Runes