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)
⚠️ Note: map is a reference type; assignment and parameter passing pass the reference, not a copy of the data.


2. Basic Syntax/Usage

Creating a Map

GO
// 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
💡 Tip: A map declared with var m map[K]V is nilwriting to it will panic, but reading from it (returns zero value) and len() (returns 0) are fine.

CRUD Operations

GO
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")
💡 Tip: Deleting a non-existent key won't cause an error; nothing happens.

Comma Ok Pattern

GO
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")
}
💡 Tip: If you don't care about the value, use _ to ignore it: _, ok := m[key].


3. Example Code

Example 1: Basic Usage (Difficulty ⭐)

Create a student grade table and perform CRUD operations.

GO
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))
}
▶ Try it Yourself

Example output (iteration order may vary):

TEXT
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.

GO
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)
        }
    }
}
▶ Try it Yourself

Output:

TEXT
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).

GO
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))
}
▶ Try it Yourself

Example output:

TEXT
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.

GO
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.

GO
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:

TEXT
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:

GO
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:

GO
// 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:

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:

GO
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


📝 Exercises

Exercise 1 (⭐)

Create a map storing 5 programming languages and their invention years, then:

  1. Add 2 new languages
  2. Modify one language's year
  3. Delete one language
  4. Use comma ok pattern to check if a language exists
  5. Iterate and print all contents

Exercise 2 (⭐⭐)

Implement a simple contacts program:

  1. Define map[string][]string, where key is contact name and value is list of phone numbers
  2. Implement functions: add contact, add phone number, search contact, delete contact
  3. Implement displaying all contacts grouped by first letter
GO
// Expected output example:
// A: Alice - [13800001111, 13900002222]
// B: Bob - [13700003333]

Exercise 3 (⭐⭐⭐)

Implement a student grade management system:

  1. Use nested map[string]map[string]float64 (class -> student -> grade)
  2. Implement functions: add grade, query all subject grades for a student, calculate class average
  3. Implement function: find the highest-scoring student for each subject across all classes
  4. Output results as formatted tables
GO
// 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)

Next Lesson

👉 07-struct - Structs

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏