Map
Map
Imagine looking up a dictionary: you enter a word (key) and can immediately find its definition (value). You don't need to flip from the first page to the last — you go directly to the target page. Go's map is exactly such a "dictionary" — it's a key-value data structure that lets you quickly look up a value by key with O(1) time complexity.
1. Core Concepts
| Concept | Description |
|---|---|
| Definition | map[KeyType]ValueType; KeyType must be a comparable type (cannot be slice, map, func) |
| Creation | make(map[K]V) or map[K]V{} literal |
| Add/Update | m[key] = value (adds if key doesn't exist, overwrites if it does) |
| Read | v := m[key] |
| Delete | delete(m, key) |
| Comma ok pattern | v, ok := m[key]; ok is false when key doesn't exist |
| Iteration | for k, v := range m (random order, not reliable) |
| Length | len(m) |
2. Basic Syntax/Usage
Creating a Map
// Method 1: Using make
m1 := make(map[string]int)
// Method 2: Literal creation with initialization
m2 := map[string]int{
"apple": 5,
"banana": 3,
}
// Method 3: Declare then assign
var m3 map[string]int // m3 is nil here, cannot assign directly
m3 = make(map[string]int) // Initialize first
m3["cherry"] = 7 // Then assign
var m map[K]V is nil — writing to it will panic, but reading from it (returns zero value) and len() (returns 0) are fine.
CRUD Operations
m := map[string]int{"a": 1, "b": 2}
// Create
m["c"] = 3
// Update
m["a"] = 10
// Read
v := m["a"] // v = 10
// Delete
delete(m, "b")
Comma Ok Pattern
m := map[string]int{"x": 42}
v, ok := m["x"] // v = 42, ok = true
v, ok = m["y"] // v = 0, ok = false (returns int's zero value)
if _, exists := m["z"]; !exists {
fmt.Println("key 'z' does not exist")
}
_ to ignore it: _, ok := m[key].
3. Example Code
Example 1: Basic Usage (Difficulty ⭐)
Create a student grade table and perform CRUD operations.
package main
import "fmt"
func main() {
// Create student grades map
scores := map[string]int{
"Alice": 90,
"Bob": 85,
}
// Add a student
scores["Charlie"] = 92
// Modify Bob's score
scores["Bob"] = 88
// Query Alice's score
fmt.Println("Alice's score:", scores["Alice"])
// Delete Charlie
delete(scores, "Charlie")
// Iterate all student scores
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
fmt.Println("Number of students:", len(scores))
}
Example output (iteration order may vary):
Alice's score: 90
Bob: 88
Alice: 90
Number of students: 2
Example 2: Intermediate Usage (Difficulty ⭐⭐)
Safe querying with comma ok pattern, iterating maps, and nested maps.
package main
import "fmt"
func main() {
// ========== Comma ok pattern ==========
fruits := map[string]int{
"apple": 5,
"banana": 3,
}
// Safe query
if count, ok := fruits["apple"]; ok {
fmt.Printf("apple has %d\n", count)
}
if count, ok := fruits["grape"]; !ok {
fmt.Println("grape doesn't exist, adding to list")
fruits["grape"] = 10
}
// ========== Range iteration ==========
fmt.Println("\nAll fruits:")
for fruit, count := range fruits {
fmt.Printf(" %s: %d\n", fruit, count)
}
// ========== Nested map (map of maps) ==========
// Class -> Student -> Score
classScores := map[string]map[string]int{
"Class A": {
"Alice": 90,
"Bob": 85,
},
"Class B": {
"Charlie": 92,
"Diana": 88,
},
}
// Query nested map
if class, ok := classScores["Class A"]; ok {
if score, ok := class["Alice"]; ok {
fmt.Printf("\nClass A Alice's score: %d\n", score)
}
}
// Iterate nested map
fmt.Println("\nAll class scores:")
for class, students := range classScores {
fmt.Printf(" %s:\n", class)
for name, score := range students {
fmt.Printf(" %s: %d\n", name, score)
}
}
}
Output:
apple has 5
grape doesn't exist, adding to list
All fruits:
apple: 5
banana: 3
grape: 10
Class A Alice's score: 90
All class scores:
Class A:
Alice: 90
Bob: 85
Class B:
Charlie: 92
Diana: 88
Example 3: Comprehensive Application (Difficulty ⭐⭐⭐)
Implement a word frequency counter with map-based data processing (sorted output, finding high-frequency words).
package main
import (
"fmt"
"sort"
"strings"
)
// WordCounter counts occurrences of each word in a text
func WordCounter(text string) map[string]int {
// Convert to lowercase and split by whitespace
words := strings.Fields(strings.ToLower(text))
// Create word frequency map
freq := make(map[string]int)
for _, word := range words {
// Remove punctuation (simple processing)
word = strings.Trim(word, ".,!?;:\"'")
if word != "" {
freq[word]++
}
}
return freq
}
// TopN returns the N most frequent words (descending by frequency)
func TopN(freq map[string]int, n int) []string {
// Convert map to a sortable slice
type wordFreq struct {
word string
count int
}
// Build slice
pairs := make([]wordFreq, 0, len(freq))
for w, c := range freq {
pairs = append(pairs, wordFreq{w, c})
}
// Sort by frequency descending
sort.Slice(pairs, func(i, j int) bool {
if pairs[i].count == pairs[j].count {
return pairs[i].word < pairs[j].word // Alphabetical when frequency is equal
}
return pairs[i].count > pairs[j].count
})
// Take top N
if n > len(pairs) {
n = len(pairs)
}
result := make([]string, n)
for i := 0; i < n; i++ {
result[i] = fmt.Sprintf("%s(%d)", pairs[i].word, pairs[i].count)
}
return result
}
// MergeFreq merges two word frequency maps
func MergeFreq(a, b map[string]int) map[string]int {
result := make(map[string]int)
// Copy a's data
for k, v := range a {
result[k] = v
}
// Accumulate b's data
for k, v := range b {
result[k] += v
}
return result
}
func main() {
text1 := "Go is great. Go is fast. Go is easy to learn."
text2 := "I love Go. Go makes programming fun and easy."
// Count word frequency
freq1 := WordCounter(text1)
freq2 := WordCounter(text2)
fmt.Println("Text 1 word frequency:")
for word, count := range freq1 {
fmt.Printf(" %s: %d\n", word, count)
}
fmt.Println("\nText 2 word frequency:")
for word, count := range freq2 {
fmt.Printf(" %s: %d\n", word, count)
}
// Merge word frequencies
merged := MergeFreq(freq1, freq2)
// Find top 5 most frequent words
fmt.Println("\nTop 5 high-frequency words after merge:")
top5 := TopN(merged, 5)
for i, item := range top5 {
fmt.Printf(" %d. %s\n", i+1, item)
}
// Count total words
totalWords := 0
for _, count := range merged {
totalWords += count
}
fmt.Printf("\nTotal words: %d, Unique words: %d\n", totalWords, len(merged))
}
Example output:
Text 1 word frequency:
go: 3
is: 3
great: 1
fast: 1
easy: 1
to: 1
learn: 1
Text 2 word frequency:
i: 1
love: 1
go: 2
makes: 1
programming: 1
fun: 1
and: 1
easy: 1
Top 5 high-frequency words after merge:
1. go(5)
2. is(3)
3. easy(2)
4. and(1)
5. fast(1)
Total words: 18, Unique words: 12
3. Common Use Cases
Case 1: Cache / Fast Lookup
Use a map to implement a simple in-memory cache to avoid redundant computation.
package main
import "fmt"
// Fibonacci sequence (with cache)
var cache = map[int]int{0: 0, 1: 1}
func fib(n int) int {
// Check cache first
if val, ok := cache[n]; ok {
return val
}
// Cache miss, compute and store in cache
result := fib(n-1) + fib(n-2)
cache[n] = result
return result
}
func main() {
for i := 0; i <= 10; i++ {
fmt.Printf("fib(%d) = %d\n", i, fib(i))
}
fmt.Println("\nCache contents:", cache)
}
Case 2: Group Statistics
Use a map to group and count data.
package main
import "fmt"
func main() {
// Student list: name -> class
students := map[string]string{
"Alice": "Class A",
"Bob": "Class B",
"Charlie": "Class A",
"Diana": "Class B",
"Eve": "Class A",
}
// Group by class
groups := make(map[string][]string)
for name, class := range students {
groups[class] = append(groups[class], name)
}
// Print grouped results
for class, members := range groups {
fmt.Printf("%s (%d people): %v\n", class, len(members), members)
}
}
Output:
Class A (3 people): [Alice Charlie Eve]
Class B (2 people): [Bob Diana]
❓ FAQ
Q1: Why is the iteration order of a map different each time?
Go intentionally randomizes map iteration order. This prevents developers from depending on map iteration order, since the internal structure may change in concurrent scenarios. If you need ordered iteration, collect keys into a slice first, sort them, then iterate:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
Q2: Is a map thread-safe?
No. Go's map is not concurrency-safe; multiple goroutines reading and writing the same map simultaneously will cause a panic. Solutions:
- Use
sync.Mutexorsync.RWMutexto protect the map - Use
sync.Mapprovided since Go 1.9 (suitable for read-heavy, write-light scenarios)
// Using sync.Map
var m sync.Map
m.Store("key", "value")
v, ok := m.Load("key")
Q3: What types can be used as map keys?
Map keys must be comparable types, including:
- Basic types:
int,float64,string,bool - Pointers
- Arrays (with comparable elements)
- Structs (with all comparable fields)
Types that cannot be keys: slice, map, func.
Q4: How do I check if a map contains a certain key?
Use the comma ok pattern:
if _, ok := m[key]; ok {
// Key exists
} else {
// Key doesn't exist
}
Don't check if a key exists by testing whether the value is a zero value, because the zero value might be a valid value.
📖 Summary
- map is Go's built-in key-value data structure; quickly looks up values by key
- Created with
makeor literalmap[K]V{};nilmaps declared withvarare read-only - Add/Update:
m[key] = value; Read:v := m[key]; Delete:delete(m, key) - Use comma ok pattern (
v, ok := m[key]) to safely check if a key exists - Use
for k, v := range mto iterate; order is random and unreliable - map is a reference type; assignment and parameter passing don't copy data
- map is not concurrency-safe; use locks or
sync.Mapfor multi-goroutine read/write - map vs slice: use map for frequent lookups, slice for ordered storage; the two are often used together
📝 Exercises
Exercise 1 (⭐)
Create a map storing 5 programming languages and their invention years, then:
- Add 2 new languages
- Modify one language's year
- Delete one language
- Use comma ok pattern to check if a language exists
- Iterate and print all contents
Exercise 2 (⭐⭐)
Implement a simple contacts program:
- Define
map[string][]string, where key is contact name and value is list of phone numbers - Implement functions: add contact, add phone number, search contact, delete contact
- Implement displaying all contacts grouped by first letter
// Expected output example:
// A: Alice - [13800001111, 13900002222]
// B: Bob - [13700003333]
Exercise 3 (⭐⭐⭐)
Implement a student grade management system:
- Use nested
map[string]map[string]float64(class -> student -> grade) - Implement functions: add grade, query all subject grades for a student, calculate class average
- Implement function: find the highest-scoring student for each subject across all classes
- Output results as formatted tables
// Expected output example:
// ========== Class Averages ==========
// Class A: 87.5
// Class B: 91.2
//
// ========== Top Scores by Subject ==========
// Math: Alice (98.0)
// English: Bob (95.0)



