Maps
Maps are Go's built-in hash table type, providing fast key-value lookups. They're essential for many programming tasks, from counting occurrences to caching data. In this lesson, you'll learn how to create, use, and manipulate maps effectively.
What is a Map?
A map is an unordered collection of key-value pairs. Each key is unique and maps to a value:
Click Run to execute your code
- Unordered - Iteration order is random
- Reference type - Like slices, maps are references
- Dynamic - Can grow as needed
- Fast lookups - O(1) average time complexity
Creating Maps
// Map literal
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 28,
}
// Using make
scores := make(map[string]int)
// Empty map literal
empty := map[string]int{}
// Nil map (can't add to it!)
var nilMap map[string]int // nil
make() or a map literal before adding elements!
Map Operations
Adding and Updating
ages := make(map[string]int)
// Add new key-value pair
ages["Alice"] = 25
// Update existing value
ages["Alice"] = 26
// Add multiple
ages["Bob"] = 30
ages["Carol"] = 28
Retrieving Values
Click Run to execute your code
value, ok := myMap[key]
if ok {
// Key exists, use value
} else {
// Key doesn't exist
}
This is the idiomatic way to check if a key exists in Go!
Deleting Keys
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
// Delete a key
delete(ages, "Alice")
// Delete non-existent key (safe, no error)
delete(ages, "NonExistent")
fmt.Println(ages) // map[Bob:30]
Iterating Over Maps
Use range to iterate over maps:
Click Run to execute your code
Sorting Map Keys
import "sort"
ages := map[string]int{
"Carol": 28,
"Alice": 25,
"Bob": 30,
}
// Get keys
keys := make([]string, 0, len(ages))
for k := range ages {
keys = append(keys, k)
}
// Sort keys
sort.Strings(keys)
// Iterate in sorted order
for _, k := range keys {
fmt.Printf("%s: %d\n", k, ages[k])
}
// Output:
// Alice: 25
// Bob: 30
// Carol: 28
Using Maps as Sets
Go doesn't have a built-in set type, but maps can be used as sets:
// Set of strings
set := make(map[string]bool)
// Add elements
set["apple"] = true
set["banana"] = true
set["orange"] = true
// Check membership
if set["apple"] {
fmt.Println("apple is in the set")
}
// Remove element
delete(set, "banana")
// Iterate over set
for item := range set {
fmt.Println(item)
}
// Alternative: use empty struct (saves memory)
set2 := make(map[string]struct{})
set2["apple"] = struct{}{}
_, exists := set2["apple"] // true
map[string]struct{}{} for sets
instead of
map[string]bool to save memory. Empty structs take zero bytes!
Figure: Go Maps - Hash table structure with buckets and common operations
Practical Map Patterns
1. Counting Occurrences
words := []string{"apple", "banana", "apple", "orange", "banana", "apple"}
counts := make(map[string]int)
for _, word := range words {
counts[word]++ // Zero value is 0, so this works!
}
fmt.Println(counts) // map[apple:3 banana:2 orange:1]
2. Grouping Data
type Person struct {
Name string
Age int
}
people := []Person{
{"Alice", 25},
{"Bob", 30},
{"Carol", 25},
{"Dave", 30},
}
// Group by age
byAge := make(map[int][]Person)
for _, p := range people {
byAge[p.Age] = append(byAge[p.Age], p)
}
// byAge[25] = [Alice, Carol]
// byAge[30] = [Bob, Dave]
3. Caching/Memoization
var cache = make(map[int]int)
func fibonacci(n int) int {
// Check cache
if val, ok := cache[n]; ok {
return val
}
// Calculate
var result int
if n <= 1 {
result = n
} else {
result = fibonacci(n-1) + fibonacci(n-2)
}
// Store in cache
cache[n] = result
return result
}
Common Mistakes
1. Writing to nil map
// β Wrong - panic: assignment to entry in nil map
var ages map[string]int
ages["Alice"] = 25 // Runtime panic!
// β
Correct - initialize first
ages := make(map[string]int)
ages["Alice"] = 25
// Or use map literal
ages := map[string]int{}
ages["Alice"] = 25
2. Not checking if key exists
// β Wrong - can't distinguish between zero value and missing key
ages := map[string]int{"Alice": 0}
age := ages["Bob"] // 0 (zero value)
// Is Bob's age 0, or does the key not exist?
// β
Correct - use comma ok idiom
if age, ok := ages["Bob"]; ok {
fmt.Println("Bob's age:", age)
} else {
fmt.Println("Bob not found")
}
3. Relying on iteration order
// β Wrong - order is random!
ages := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 28,
}
for name := range ages {
fmt.Println(name) // Random order each time!
}
// β
Correct - sort keys if order matters
keys := make([]string, 0, len(ages))
for k := range ages {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, ages[k])
}
4. Concurrent map access
// β Wrong - concurrent map writes cause panic
m := make(map[int]int)
go func() { m[1] = 1 }()
go func() { m[2] = 2 }() // Race condition!
// β
Correct - use sync.Mutex or sync.Map
var mu sync.Mutex
m := make(map[int]int)
go func() {
mu.Lock()
m[1] = 1
mu.Unlock()
}()
Exercise: Word Frequency Counter
Task: Create a word frequency counter.
Requirements:
- Count word frequencies in a sentence
- Find the most common word
- Print words in alphabetical order with counts
- Test with: "the quick brown fox jumps over the lazy dog the fox"
Show Solution
package main
import (
"fmt"
"sort"
"strings"
)
func main() {
text := "the quick brown fox jumps over the lazy dog the fox"
// Split into words
words := strings.Fields(text)
// Count frequencies
freq := make(map[string]int)
for _, word := range words {
freq[word]++
}
// Find most common word
maxCount := 0
mostCommon := ""
for word, count := range freq {
if count > maxCount {
maxCount = count
mostCommon = word
}
}
fmt.Printf("Most common word: '%s' (appears %d times)\n\n", mostCommon, maxCount)
// Get sorted keys
keys := make([]string, 0, len(freq))
for k := range freq {
keys = append(keys, k)
}
sort.Strings(keys)
// Print in alphabetical order
fmt.Println("Word frequencies (alphabetical):")
for _, word := range keys {
fmt.Printf(" %s: %d\n", word, freq[word])
}
// Bonus: Total unique words
fmt.Printf("\nTotal unique words: %d\n", len(freq))
fmt.Printf("Total words: %d\n", len(words))
}
Summary
- Maps store key-value pairs with fast lookups
- Create maps with make() or map literals
- Nil maps can be read but not written to
- Comma ok idiom checks if a key exists
- delete() removes keys from maps
- Iteration order is random - sort keys if needed
- Maps are reference types - passed by reference
- Not safe for concurrent use - use mutex or sync.Map
What's Next?
Now that you understand maps, you're ready to learn about Structs & Methods. Structs let you create custom types, and methods let you add behavior to those typesβthe foundation of object-oriented programming in Go!
Enjoying these tutorials?