Maps

Why This Matters - The Foundation of Key-Value Storage

Maps are essential data structures that enable fast lookups, caching, and data relationships. They're the backbone of countless applications - from web servers routing requests to databases indexing records.

Understanding maps is crucial because:

  • Fast Lookups: O(1) average time complexity for data retrieval
  • Flexible Keys: Support various comparable types as keys
  • Dynamic Nature: Add and remove entries at runtime
  • Memory Efficiency: Store only what you need, grow as needed
  • Real-World Modeling: Perfect for representing relationships and configurations

Real-World Impact: Every web framework uses maps for routing, every caching system relies on maps for storage, every JSON parser uses maps for dynamic data, and every configuration system uses maps for settings. Mastering maps means you can build efficient, scalable applications.

Learning Objectives

By the end of this article, you will:

  • ✅ Understand map internals and hash table implementation
  • ✅ Master key requirements and comparison rules
  • ✅ Implement efficient lookup patterns and avoid common pitfalls
  • ✅ Use map literals and built-in functions effectively
  • ✅ Handle concurrent access safely and correctly
  • ✅ Apply performance optimization techniques
  • ✅ Build real-world applications with map-based patterns
  • ✅ Debug common map-related issues in production code

Core Concepts - Understanding Map Fundamentals

Hash Tables Under the Hood

Maps are implemented as hash tables - collections of key-value pairs where keys are hashed to determine storage location.

 1// Conceptual map structure
 2type Map struct {
 3    buckets    []bucket  // Array of buckets
 4    count      int       // Number of stored elements
 5    seed       uintptr   // Hash seed for security
 6}
 7
 8type bucket struct {
 9    tophash [8]uint8  // Top 8 bits of hash
10    entries []entry      // Key-value pairs
11}
12
13type entry struct {
14    key   KeyType
15    value ValueType
16}

Key Characteristics:

  • Dynamic sizing: Grows when load factor gets too high
  • Collision resolution: Handles hash collisions efficiently
  • Security: Randomized hash seeds prevent DoS attacks
  • Memory overhead: More memory than simple arrays but provides flexibility

Map Declaration and Initialization

1// Declaration styles
2var m1 map[string]int                          // nil map
3m2 := make(map[string]int)                     // empty map
4m3 := map[string]int{"go": 1, "python": 2}   // literal with data
5m4 := make(map[string]int, 1000)             // pre-allocate capacity

Why Different Methods:

  • var m map[K]V creates nil map - needs make() before use
  • make(map[K]V) creates empty, ready-to-use map
  • map[K]V{} literal creates map with initial data
  • make(map[K]V, cap) pre-allocates for performance

Understanding Map Keys

Not all types can be map keys. Keys must be comparable - meaning they support the == operator.

Valid Key Types:

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Integer keys
 8    ages := map[int]string{
 9        1: "one",
10        2: "two",
11        3: "three",
12    }
13    fmt.Println("Integer keys:", ages)
14
15    // String keys (most common)
16    capitals := map[string]string{
17        "USA":    "Washington DC",
18        "France": "Paris",
19        "Japan":  "Tokyo",
20    }
21    fmt.Println("String keys:", capitals)
22
23    // Struct keys (all fields must be comparable)
24    type Point struct {
25        X, Y int
26    }
27    distances := map[Point]float64{
28        {0, 0}: 0.0,
29        {1, 0}: 1.0,
30        {0, 1}: 1.0,
31    }
32    fmt.Println("Struct keys:", distances)
33
34    // Array keys (arrays are comparable if their elements are)
35    type RGB [3]uint8
36    colors := map[RGB]string{
37        {255, 0, 0}:   "red",
38        {0, 255, 0}:   "green",
39        {0, 0, 255}:   "blue",
40    }
41    fmt.Println("Array keys:", colors)
42
43    // Pointer keys
44    x, y := 10, 20
45    ptrMap := map[*int]string{
46        &x: "pointer to x",
47        &y: "pointer to y",
48    }
49    fmt.Println("Pointer keys:", ptrMap)
50}

Invalid Key Types:

1// ❌ These won't compile
2var m1 map[[]int]string        // Slices are not comparable
3var m2 map[map[string]int]bool // Maps are not comparable
4var m3 map[func()]int          // Functions are not comparable

The Zero Value Behavior

Maps have special zero-value behavior that differs from other types:

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    var nilMap map[string]int  // nil map
 8    emptyMap := make(map[string]int)  // empty but non-nil map
 9
10    fmt.Printf("nilMap == nil: %t\n", nilMap == nil)
11    fmt.Printf("emptyMap == nil: %t\n", emptyMap == nil)
12
13    // Reading from nil map returns zero value (no panic)
14    value := nilMap["key"]
15    fmt.Printf("Reading from nil map: %d\n", value)
16
17    // Checking existence works on nil maps
18    _, exists := nilMap["key"]
19    fmt.Printf("Key exists in nil map: %t\n", exists)
20
21    // Length of nil map is 0
22    fmt.Printf("Length of nil map: %d\n", len(nilMap))
23
24    // ❌ Writing to nil map causes panic!
25    // nilMap["key"] = 42  // This would crash
26
27    // ✅ Writing to empty map works fine
28    emptyMap["key"] = 42
29    fmt.Printf("Empty map after write: %v\n", emptyMap)
30}

Important: You can read from a nil map, but writing to it causes a panic. Always initialize maps before writing!

Practical Examples - Map Operations in Action

Example 1: Basic Map Operations

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // Map creation and initialization
 8    prices := map[string]float64{
 9        "apple":  0.99,
10        "banana": 0.59,
11        "cherry": 2.99,
12    }
13
14    fmt.Println("Initial prices:", prices)
15
16    // Adding/updating entries
17    prices["orange"] = 1.49           // Add new entry
18    prices["apple"] = 1.09             // Update existing entry
19    fmt.Println("After updates:", prices)
20
21    // Reading entries
22    applePrice, exists := prices["apple"]
23    if exists {
24        fmt.Printf("Apple price: $%.2f\n", applePrice)
25    }
26
27    grapePrice, exists := prices["grape"]
28    if !exists {
29        fmt.Println("Grape not found in price list")
30    }
31
32    // Deleting entries
33    delete(prices, "banana")
34    fmt.Println("After deleting banana:", prices)
35
36    // Map properties
37    fmt.Printf("Map length: %d\n", len(prices))
38}

Example 2: Map Value Types and Behavior

 1// run
 2package main
 3
 4import "fmt"
 5
 6type Address struct {
 7    Street  string
 8    City    string
 9    Country string
10}
11
12func main() {
13    // Map with slice values
14    users := map[string][]string{
15        "alice":   {"reading", "hiking"},
16        "bob":     {"gaming", "coding"},
17        "charlie": {"music"},
18    }
19
20    // Adding to slice in map
21    users["alice"] = append(users["alice"], "cooking")
22    fmt.Printf("Alice's hobbies: %v\n", users["alice"])
23
24    // Map with struct values
25    addresses := make(map[string]Address)
26    addresses["home"] = Address{
27        Street:  "123 Main St",
28        City:    "Anytown",
29        Country: "USA",
30    }
31
32    // Modifying struct in map requires reassignment
33    home := addresses["home"]
34    home.City = "Newtown"
35    addresses["home"] = home
36    fmt.Printf("Home address: %+v\n", addresses["home"])
37
38    // Map with pointer values
39    profiles := make(map[string]*Address)
40    profiles["work"] = &Address{
41        Street:  "456 Office Blvd",
42        City:    "Business City",
43        Country: "USA",
44    }
45
46    // Can modify directly through pointer
47    profiles["work"].Street = "789 Corporate Ave"
48    fmt.Printf("Work address: %+v\n", *profiles["work"])
49}

Example 3: Map Iteration and Key Order

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "sort"
 7)
 8
 9func main() {
10    // Create a map
11    scores := map[string]int{
12        "alice":   95,
13        "bob":     87,
14        "charlie": 92,
15        "diana":   88,
16        "eve":     91,
17    }
18
19    fmt.Println("Map iteration (random order):")
20    for name, score := range scores {
21        fmt.Printf("%s: %d\n", name, score)
22    }
23
24    // Sorted iteration by keys
25    fmt.Println("\nSorted by name:")
26    names := make([]string, 0, len(scores))
27    for name := range scores {
28        names = append(names, name)
29    }
30    sort.Strings(names)
31
32    for _, name := range names {
33        fmt.Printf("%s: %d\n", name, scores[name])
34    }
35
36    // Sorted iteration by values
37    fmt.Println("\nSorted by score:")
38    type Score struct {
39        name  string
40        score int
41    }
42
43    sortedScores := make([]Score, 0, len(scores))
44    for name, score := range scores {
45        sortedScores = append(sortedScores, Score{name, score})
46    }
47
48    // Sort by score (descending)
49    sort.Slice(sortedScores, func(i, j int) bool {
50        return sortedScores[i].score > sortedScores[j].score
51    })
52
53    for _, item := range sortedScores {
54        fmt.Printf("%s: %d\n", item.name, item.score)
55    }
56}

Important Note: Map iteration order is intentionally randomized in Go to prevent code from depending on a specific order. If you need ordered iteration, extract keys to a slice and sort them first.

Example 4: Nested Maps and Complex Data

 1// run
 2package main
 3
 4import "fmt"
 5
 6type Address struct {
 7    Street  string
 8    City    string
 9    Country string
10}
11
12func main() {
13    // Nested maps: organizing hierarchical data
14    company := map[string]map[string]float64{
15        "engineering": {
16            "alice":   95000,
17            "bob":     87000,
18            "charlie": 102000,
19        },
20        "marketing": {
21            "diana": 78000,
22            "eve":   82000,
23        },
24        "sales": {
25            "frank": 88000,
26        },
27    }
28
29    // Accessing nested data
30    engineering := company["engineering"]
31    aliceSalary := engineering["alice"]
32    fmt.Printf("Alice's salary: $%.2f\n", aliceSalary)
33
34    // Adding to nested structure
35    if _, exists := company["hr"]; !exists {
36        company["hr"] = make(map[string]float64)
37    }
38    company["hr"]["grace"] = 72000
39
40    // Calculating department totals
41    for dept, employees := range company {
42        var total float64
43        for _, salary := range employees {
44            total += salary
45        }
46        fmt.Printf("%s department: %d employees, $%.2f total\n",
47            dept, len(employees), total)
48    }
49
50    // Complex data structure example
51    type Student struct {
52        Name    string
53        Grades  map[string]float64
54        Address Address
55    }
56
57    students := make(map[int]Student)
58    students[1001] = Student{
59        Name: "John Doe",
60        Grades: map[string]float64{
61            "math":    95.5,
62            "science": 87.3,
63            "english": 92.1,
64        },
65        Address: Address{
66            Street:  "123 University Ave",
67            City:    "College Town",
68            Country: "USA",
69        },
70    }
71
72    student := students[1001]
73    fmt.Printf("\nStudent %s's math grade: %.1f\n",
74        student.Name, student.Grades["math"])
75}

Example 5: Map as Set Implementation

  1// run
  2package main
  3
  4import "fmt"
  5
  6// Set implementation using map
  7type Set[T comparable] struct {
  8    items map[T]struct{}
  9}
 10
 11func NewSet[T comparable]() *Set[T] {
 12    return &Set[T]{
 13        items: make(map[T]struct{}),
 14    }
 15}
 16
 17func (s *Set[T]) Add(item T) {
 18    s.items[item] = struct{}{}
 19}
 20
 21func (s *Set[T]) Remove(item T) {
 22    delete(s.items, item)
 23}
 24
 25func (s *Set[T]) Contains(item T) bool {
 26    _, exists := s.items[item]
 27    return exists
 28}
 29
 30func (s *Set[T]) Size() int {
 31    return len(s.items)
 32}
 33
 34func (s *Set[T]) Items() []T {
 35    items := make([]T, 0, len(s.items))
 36    for item := range s.items {
 37        items = append(items, item)
 38    }
 39    return items
 40}
 41
 42// Set operations
 43func (s *Set[T]) Union(other *Set[T]) *Set[T] {
 44    result := NewSet[T]()
 45
 46    // Add all items from this set
 47    for item := range s.items {
 48        result.Add(item)
 49    }
 50
 51    // Add all items from other set
 52    for item := range other.items {
 53        result.Add(item)
 54    }
 55
 56    return result
 57}
 58
 59func (s *Set[T]) Intersection(other *Set[T]) *Set[T] {
 60    result := NewSet[T]()
 61
 62    // Add only items that exist in both sets
 63    for item := range s.items {
 64        if other.Contains(item) {
 65            result.Add(item)
 66        }
 67    }
 68
 69    return result
 70}
 71
 72func main() {
 73    // Integer set example
 74    numbers := NewSet[int]()
 75    numbers.Add(1)
 76    numbers.Add(2)
 77    numbers.Add(3)
 78    numbers.Add(2) // Duplicate - won't be added
 79
 80    fmt.Printf("Numbers set: %v\n", numbers.Items())
 81    fmt.Printf("Contains 2: %t\n", numbers.Contains(2))
 82    fmt.Printf("Contains 4: %t\n", numbers.Contains(4))
 83
 84    // String set example
 85    fruits := NewSet[string]()
 86    fruits.Add("apple")
 87    fruits.Add("banana")
 88    fruits.Add("cherry")
 89
 90    colors := NewSet[string]()
 91    colors.Add("red")
 92    colors.Add("green")
 93    colors.Add("blue")
 94
 95    // Set operations
 96    union := fruits.Union(colors)
 97    intersection := fruits.Intersection(colors)
 98
 99    fmt.Printf("Union: %v\n", union.Items())
100    fmt.Printf("Intersection: %v\n", intersection.Items())
101    fmt.Printf("Intersection size: %d\n", intersection.Size())
102}

Why struct{}? Using struct{}{} as the value type takes zero bytes of memory. We only care about the keys (set membership), not the values.

Advanced Map Patterns

Pattern 1: Map as Frequency Counter

A fundamental pattern for text processing, analytics, and data aggregation:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "sort"
 7    "strings"
 8)
 9
10func wordFrequency(text string) map[string]int {
11    words := strings.Fields(strings.ToLower(text))
12    freq := make(map[string]int, len(words))
13
14    for _, word := range words {
15        freq[word]++
16    }
17
18    return freq
19}
20
21func topN(freq map[string]int, n int) []string {
22    items := make([]string, 0, len(freq))
23    for word := range freq {
24        items = append(items, word)
25    }
26
27    sort.Slice(items, func(i, j int) bool {
28        return freq[items[i]] > freq[items[j]]
29    })
30
31    if len(items) > n {
32        items = items[:n]
33    }
34
35    return items
36}
37
38func main() {
39    text := "the quick brown fox jumps over the lazy dog the fox was quick and the dog was lazy"
40
41    freq := wordFrequency(text)
42    fmt.Println("Word frequencies:")
43    for word, count := range freq {
44        fmt.Printf("%s: %d\n", word, count)
45    }
46
47    fmt.Println("\nTop 5 words:")
48    top := topN(freq, 5)
49    for i, word := range top {
50        fmt.Printf("%d. %s (%d occurrences)\n", i+1, word, freq[word])
51    }
52}

Pattern 2: Map Caching Pattern

Memoization improves performance by caching expensive computations:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "sync"
 7    "time"
 8)
 9
10type Cache struct {
11    data map[int]int
12    mu   sync.RWMutex
13}
14
15func NewCache() *Cache {
16    return &Cache{
17        data: make(map[int]int),
18    }
19}
20
21func (c *Cache) Get(key int) (int, bool) {
22    c.mu.RLock()
23    defer c.mu.RUnlock()
24
25    value, exists := c.data[key]
26    return value, exists
27}
28
29func (c *Cache) Set(key, value int) {
30    c.mu.Lock()
31    defer c.mu.Unlock()
32
33    c.data[key] = value
34}
35
36// Fibonacci with caching
37func fibonacci(n int, cache *Cache) int {
38    if n <= 1 {
39        return n
40    }
41
42    if cached, exists := cache.Get(n); exists {
43        return cached
44    }
45
46    result := fibonacci(n-1, cache) + fibonacci(n-2, cache)
47    cache.Set(n, result)
48    return result
49}
50
51func main() {
52    cache := NewCache()
53
54    // Measure with cache
55    start := time.Now()
56    result := fibonacci(40, cache)
57    duration := time.Since(start)
58
59    fmt.Printf("Fibonacci(40) = %d\n", result)
60    fmt.Printf("Computed in: %v\n", duration)
61    fmt.Printf("Cache size: %d entries\n", len(cache.data))
62}

Pattern 3: Map for Configuration Management

Maps excel at managing application configuration:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7)
 8
 9type Config struct {
10    settings map[string]interface{}
11    defaults map[string]interface{}
12}
13
14func NewConfig() *Config {
15    return &Config{
16        settings: make(map[string]interface{}),
17        defaults: map[string]interface{}{
18            "timeout":      30 * time.Second,
19            "max_retries":  3,
20            "buffer_size":  1024,
21            "debug":        false,
22            "server_addr":  "localhost:8080",
23        },
24    }
25}
26
27func (c *Config) Set(key string, value interface{}) {
28    c.settings[key] = value
29}
30
31func (c *Config) Get(key string) interface{} {
32    // Check user settings first
33    if value, exists := c.settings[key]; exists {
34        return value
35    }
36
37    // Fall back to defaults
38    if value, exists := c.defaults[key]; exists {
39        return value
40    }
41
42    return nil
43}
44
45func (c *Config) GetString(key string) string {
46    if value := c.Get(key); value != nil {
47        if str, ok := value.(string); ok {
48            return str
49        }
50    }
51    return ""
52}
53
54func (c *Config) GetInt(key string) int {
55    if value := c.Get(key); value != nil {
56        if num, ok := value.(int); ok {
57            return num
58        }
59    }
60    return 0
61}
62
63func (c *Config) GetDuration(key string) time.Duration {
64    if value := c.Get(key); value != nil {
65        if duration, ok := value.(time.Duration); ok {
66            return duration
67        }
68    }
69    return 0
70}
71
72func (c *Config) GetBool(key string) bool {
73    if value := c.Get(key); value != nil {
74        if b, ok := value.(bool); ok {
75            return b
76        }
77    }
78    return false
79}
80
81func main() {
82    config := NewConfig()
83
84    // Use defaults
85    fmt.Printf("Default timeout: %v\n", config.GetDuration("timeout"))
86    fmt.Printf("Default retries: %d\n", config.GetInt("max_retries"))
87
88    // Override settings
89    config.Set("timeout", 60*time.Second)
90    config.Set("debug", true)
91
92    fmt.Printf("\nAfter overrides:\n")
93    fmt.Printf("Timeout: %v\n", config.GetDuration("timeout"))
94    fmt.Printf("Debug: %t\n", config.GetBool("debug"))
95    fmt.Printf("Server: %s\n", config.GetString("server_addr"))
96}

Pattern 4: Map-Based State Machine

Maps can elegantly represent state transitions:

 1// run
 2package main
 3
 4import "fmt"
 5
 6type State string
 7type Event string
 8
 9const (
10    StatePending   State = "pending"
11    StateActive    State = "active"
12    StateSuspended State = "suspended"
13    StateClosed    State = "closed"
14)
15
16const (
17    EventActivate Event = "activate"
18    EventSuspend  Event = "suspend"
19    EventResume   Event = "resume"
20    EventClose    Event = "close"
21)
22
23type StateMachine struct {
24    current     State
25    transitions map[State]map[Event]State
26}
27
28func NewStateMachine() *StateMachine {
29    return &StateMachine{
30        current: StatePending,
31        transitions: map[State]map[Event]State{
32            StatePending: {
33                EventActivate: StateActive,
34                EventClose:    StateClosed,
35            },
36            StateActive: {
37                EventSuspend: StateSuspended,
38                EventClose:   StateClosed,
39            },
40            StateSuspended: {
41                EventResume: StateActive,
42                EventClose:  StateClosed,
43            },
44            StateClosed: {},
45        },
46    }
47}
48
49func (sm *StateMachine) Trigger(event Event) (bool, error) {
50    if validTransitions, exists := sm.transitions[sm.current]; exists {
51        if nextState, valid := validTransitions[event]; valid {
52            fmt.Printf("State transition: %s -> %s (event: %s)\n",
53                sm.current, nextState, event)
54            sm.current = nextState
55            return true, nil
56        }
57    }
58
59    return false, fmt.Errorf("invalid transition: %s in state %s",
60        event, sm.current)
61}
62
63func (sm *StateMachine) Current() State {
64    return sm.current
65}
66
67func main() {
68    sm := NewStateMachine()
69
70    fmt.Printf("Initial state: %s\n\n", sm.Current())
71
72    // Valid transitions
73    sm.Trigger(EventActivate)
74    sm.Trigger(EventSuspend)
75    sm.Trigger(EventResume)
76
77    // Invalid transition
78    _, err := sm.Trigger(EventActivate)
79    if err != nil {
80        fmt.Printf("Error: %v\n", err)
81    }
82
83    // Final transition
84    sm.Trigger(EventClose)
85    fmt.Printf("\nFinal state: %s\n", sm.Current())
86}

Pattern 5: Inverted Index

Essential for search functionality and text analysis:

  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "strings"
  7)
  8
  9type Document struct {
 10    ID      int
 11    Content string
 12}
 13
 14type InvertedIndex struct {
 15    index map[string][]int // word -> document IDs
 16}
 17
 18func NewInvertedIndex() *InvertedIndex {
 19    return &InvertedIndex{
 20        index: make(map[string][]int),
 21    }
 22}
 23
 24func (idx *InvertedIndex) Add(doc Document) {
 25    words := strings.Fields(strings.ToLower(doc.Content))
 26
 27    // Track unique words in this document
 28    seen := make(map[string]bool)
 29
 30    for _, word := range words {
 31        // Remove punctuation
 32        word = strings.Trim(word, ".,!?;:")
 33
 34        if !seen[word] {
 35            idx.index[word] = append(idx.index[word], doc.ID)
 36            seen[word] = true
 37        }
 38    }
 39}
 40
 41func (idx *InvertedIndex) Search(query string) []int {
 42    query = strings.ToLower(query)
 43    return idx.index[query]
 44}
 45
 46func (idx *InvertedIndex) SearchMultiple(queries []string) []int {
 47    if len(queries) == 0 {
 48        return nil
 49    }
 50
 51    // Start with documents matching first query
 52    resultMap := make(map[int]int)
 53    for _, docID := range idx.Search(queries[0]) {
 54        resultMap[docID] = 1
 55    }
 56
 57    // Intersect with documents matching other queries
 58    for _, query := range queries[1:] {
 59        for _, docID := range idx.Search(query) {
 60            resultMap[docID]++
 61        }
 62    }
 63
 64    // Return documents matching all queries
 65    var results []int
 66    for docID, count := range resultMap {
 67        if count == len(queries) {
 68            results = append(results, docID)
 69        }
 70    }
 71
 72    return results
 73}
 74
 75func main() {
 76    index := NewInvertedIndex()
 77
 78    // Add documents
 79    docs := []Document{
 80        {1, "The quick brown fox jumps over the lazy dog"},
 81        {2, "A quick brown dog runs fast"},
 82        {3, "The lazy cat sleeps all day"},
 83        {4, "A brown fox and a brown dog"},
 84    }
 85
 86    for _, doc := range docs {
 87        index.Add(doc)
 88    }
 89
 90    // Single word search
 91    fmt.Println("Documents containing 'brown':")
 92    fmt.Println(index.Search("brown"))
 93
 94    fmt.Println("\nDocuments containing 'lazy':")
 95    fmt.Println(index.Search("lazy"))
 96
 97    // Multi-word search
 98    fmt.Println("\nDocuments containing both 'brown' and 'dog':")
 99    fmt.Println(index.SearchMultiple([]string{"brown", "dog"}))
100}

Common Patterns and Pitfalls

Common Pitfall 1: Nil Map Access

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    var m map[string]int  // nil map
 8
 9    // ✅ Reading is safe
10    value := m["key"]
11    fmt.Printf("Value: %d\n", value)  // Returns zero value
12
13    // ✅ Checking existence is safe
14    _, exists := m["key"]
15    fmt.Printf("Exists: %t\n", exists)
16
17    // ✅ Length is safe
18    fmt.Printf("Length: %d\n", len(m))
19
20    // ✅ Ranging over nil map is safe (does nothing)
21    for k, v := range m {
22        fmt.Printf("%s: %d\n", k, v)
23    }
24
25    // ❌ Writing panics!
26    // m["key"] = 1  // panic: assignment to entry in nil map
27
28    // ✅ Initialize before writing
29    m = make(map[string]int)
30    m["key"] = 1
31    fmt.Printf("After initialization: %v\n", m)
32}

Common Pitfall 2: Concurrent Access

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "sync"
 7)
 8
 9func main() {
10    // ❌ UNSAFE: Concurrent writes without synchronization
11    unsafeDemo := func() {
12        m := make(map[int]int)
13        var wg sync.WaitGroup
14
15        // This would cause a "concurrent map writes" panic
16        // Commented out for safety
17        /*
18        for i := 0; i < 10; i++ {
19            wg.Add(1)
20            go func(id int) {
21                defer wg.Done()
22                m[id] = id * 2  // ❌ Unsafe!
23            }(i)
24        }
25        wg.Wait()
26        */
27        fmt.Println("Unsafe concurrent access (commented out for safety)")
28    }
29
30    // ✅ SAFE: Use sync.Map for simple cases
31    safeWithSyncMap := func() {
32        var m sync.Map
33        var wg sync.WaitGroup
34
35        for i := 0; i < 10; i++ {
36            wg.Add(1)
37            go func(id int) {
38                defer wg.Done()
39                m.Store(id, id*2)  // ✅ Safe!
40            }(i)
41        }
42        wg.Wait()
43
44        fmt.Println("Safe with sync.Map:")
45        m.Range(func(key, value interface{}) bool {
46            fmt.Printf("%d: %d\n", key, value)
47            return true
48        })
49    }
50
51    // ✅ SAFE: Use mutex for complex operations
52    safeWithMutex := func() {
53        type SafeMap struct {
54            mu   sync.RWMutex
55            data map[int]int
56        }
57
58        sm := &SafeMap{
59            data: make(map[int]int),
60        }
61
62        var wg sync.WaitGroup
63        for i := 0; i < 10; i++ {
64            wg.Add(1)
65            go func(id int) {
66                defer wg.Done()
67                sm.mu.Lock()
68                sm.data[id] = id * 2  // ✅ Safe!
69                sm.mu.Unlock()
70            }(i)
71        }
72        wg.Wait()
73
74        fmt.Println("\nSafe with mutex:")
75        sm.mu.RLock()
76        for k, v := range sm.data {
77            fmt.Printf("%d: %d\n", k, v)
78        }
79        sm.mu.RUnlock()
80    }
81
82    unsafeDemo()
83    safeWithSyncMap()
84    safeWithMutex()
85}

Common Pitfall 3: Map Modification During Iteration

 1// run
 2package main
 3
 4import "fmt"
 5
 6func main() {
 7    // ❌ PROBLEMATIC: Deleting during iteration
 8    unsafeDelete := func() {
 9        m := map[string]int{
10            "a": 1,
11            "b": 2,
12            "c": 3,
13            "d": 4,
14        }
15
16        fmt.Println("Deleting during iteration (may skip elements):")
17        for k := range m {
18            if k == "b" || k == "c" {
19                delete(m, k)  // May cause unexpected behavior
20            }
21        }
22        fmt.Printf("Result: %v\n", m)
23    }
24
25    // ✅ SAFE: Collect keys first, then delete
26    safeDelete := func() {
27        m := map[string]int{
28            "a": 1,
29            "b": 2,
30            "c": 3,
31            "d": 4,
32        }
33
34        // Collect keys to delete
35        toDelete := []string{}
36        for k := range m {
37            if k == "b" || k == "c" {
38                toDelete = append(toDelete, k)
39            }
40        }
41
42        // Delete after iteration
43        for _, k := range toDelete {
44            delete(m, k)
45        }
46
47        fmt.Println("\nSafe deletion:")
48        fmt.Printf("Result: %v\n", m)
49    }
50
51    unsafeDelete()
52    safeDelete()
53}

Common Pitfall 4: Map Equality Comparison

 1// run
 2package main
 3
 4import "fmt"
 5
 6func mapsEqual[K, V comparable](m1, m2 map[K]V) bool {
 7    if len(m1) != len(m2) {
 8        return false
 9    }
10
11    for k, v1 := range m1 {
12        v2, exists := m2[k]
13        if !exists || v1 != v2 {
14            return false
15        }
16    }
17
18    return true
19}
20
21func main() {
22    m1 := map[string]int{"a": 1, "b": 2}
23    m2 := map[string]int{"a": 1, "b": 2}
24    m3 := map[string]int{"a": 1, "b": 3}
25
26    // ❌ Cannot compare maps directly
27    // fmt.Println(m1 == m2)  // Compile error!
28
29    // ✅ Use custom comparison function
30    fmt.Printf("m1 == m2: %t\n", mapsEqual(m1, m2))
31    fmt.Printf("m1 == m3: %t\n", mapsEqual(m1, m3))
32}

Integration and Mastery - Building Real Applications

Example 1: Multi-Level Caching System

  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "sync"
  7    "time"
  8)
  9
 10type CacheItem struct {
 11    Value      interface{}
 12    ExpiresAt time.Time
 13    AccessCount int
 14    LastAccess  time.Time
 15}
 16
 17type MultiLevelCache struct {
 18    // L1: Fast, small cache
 19    l1Cache map[string]*CacheItem
 20    l1Size  int
 21    l1Mu    sync.RWMutex
 22
 23    // L2: Slower, larger cache
 24    l2Cache map[string]*CacheItem
 25    l2Size  int
 26    l2Mu    sync.RWMutex
 27
 28    // Statistics
 29    hits   int64
 30    misses int64
 31}
 32
 33func NewMultiLevelCache(l1Size, l2Size int) *MultiLevelCache {
 34    return &MultiLevelCache{
 35        l1Cache: make(map[string]*CacheItem, l1Size),
 36        l2Cache: make(map[string]*CacheItem, l2Size),
 37        l1Size:  l1Size,
 38        l2Size:  l2Size,
 39    }
 40}
 41
 42func (c *MultiLevelCache) Get(key string) (interface{}, bool) {
 43    // Try L1 cache first
 44    c.l1Mu.RLock()
 45    if item, exists := c.l1Cache[key]; exists {
 46        if time.Now().Before(item.ExpiresAt) {
 47            item.AccessCount++
 48            item.LastAccess = time.Now()
 49            c.l1Mu.RUnlock()
 50            c.hits++
 51            return item.Value, true
 52        }
 53        // Item expired, remove it
 54        c.l1Mu.RUnlock()
 55        c.l1Mu.Lock()
 56        delete(c.l1Cache, key)
 57        c.l1Mu.Unlock()
 58    } else {
 59        c.l1Mu.RUnlock()
 60    }
 61
 62    // Try L2 cache
 63    c.l2Mu.RLock()
 64    if item, exists := c.l2Cache[key]; exists {
 65        if time.Now().Before(item.ExpiresAt) {
 66            item.AccessCount++
 67            item.LastAccess = time.Now()
 68            value := item.Value
 69            c.l2Mu.RUnlock()
 70
 71            // Promote to L1 if there's space
 72            c.promoteToL1(key, item)
 73            c.hits++
 74            return value, true
 75        }
 76        // Item expired
 77        c.l2Mu.RUnlock()
 78        c.l2Mu.Lock()
 79        delete(c.l2Cache, key)
 80        c.l2Mu.Unlock()
 81    } else {
 82        c.l2Mu.RUnlock()
 83    }
 84
 85    c.misses++
 86    return nil, false
 87}
 88
 89func (c *MultiLevelCache) Set(key string, value interface{}, ttl time.Duration) {
 90    expiresAt := time.Now().Add(ttl)
 91    item := &CacheItem{
 92        Value:      value,
 93        ExpiresAt:  expiresAt,
 94        AccessCount: 1,
 95        LastAccess:  time.Now(),
 96    }
 97
 98    // Try to store in L1 first
 99    c.l1Mu.Lock()
100    if len(c.l1Cache) < c.l1Size {
101        c.l1Cache[key] = item
102        c.l1Mu.Unlock()
103        return
104    }
105    c.l1Mu.Unlock()
106
107    // Store in L2
108    c.l2Mu.Lock()
109    if len(c.l2Cache) >= c.l2Size {
110        c.evictFromL2()
111    }
112    c.l2Cache[key] = item
113    c.l2Mu.Unlock()
114}
115
116func (c *MultiLevelCache) promoteToL1(key string, item *CacheItem) {
117    c.l1Mu.Lock()
118    defer c.l1Mu.Unlock()
119
120    // Evict if necessary
121    if len(c.l1Cache) >= c.l1Size {
122        c.evictFromL1()
123    }
124
125    c.l1Cache[key] = item
126}
127
128func (c *MultiLevelCache) evictFromL1() {
129    // Evict least recently used item
130    var oldestKey string
131    var oldestTime time.Time
132
133    for key, item := range c.l1Cache {
134        if oldestKey == "" || item.LastAccess.Before(oldestTime) {
135            oldestKey = key
136            oldestTime = item.LastAccess
137        }
138    }
139
140    if oldestKey != "" {
141        delete(c.l1Cache, oldestKey)
142    }
143}
144
145func (c *MultiLevelCache) evictFromL2() {
146    // Evict least recently used item
147    var oldestKey string
148    var oldestTime time.Time
149
150    for key, item := range c.l2Cache {
151        if oldestKey == "" || item.LastAccess.Before(oldestTime) {
152            oldestKey = key
153            oldestTime = item.LastAccess
154        }
155    }
156
157    if oldestKey != "" {
158        delete(c.l2Cache, oldestKey)
159    }
160}
161
162func (c *MultiLevelCache) Stats() (int64, int64, float64) {
163    total := c.hits + c.misses
164    hitRate := 0.0
165    if total > 0 {
166        hitRate = float64(c.hits) / float64(total) * 100
167    }
168    return c.hits, c.misses, hitRate
169}
170
171func main() {
172    cache := NewMultiLevelCache(10, 100) // L1: 10 items, L2: 100 items
173
174    fmt.Println("=== Multi-Level Cache Demo ===")
175
176    // Test cache operations
177    keys := []string{"user:1", "user:2", "config:app", "session:abc123", "product:12345"}
178
179    // Set some values
180    for _, key := range keys {
181        cache.Set(key, "value-for-"+key, time.Minute*5)
182        fmt.Printf("Cached: %s\n", key)
183    }
184
185    // Retrieve values
186    for _, key := range keys {
187        if value, found := cache.Get(key); found {
188            fmt.Printf("Retrieved: %s = %v\n", key, value)
189        } else {
190            fmt.Printf("Not found: %s\n", key)
191        }
192    }
193
194    // Add more items to trigger evictions
195    for i := 0; i < 150; i++ {
196        cache.Set(fmt.Sprintf("temp:%d", i), i, time.Minute)
197    }
198
199    // Check cache stats
200    hits, misses, hitRate := cache.Stats()
201    fmt.Printf("\nCache Statistics:\n")
202    fmt.Printf("Hits: %d\n", hits)
203    fmt.Printf("Misses: %d\n", misses)
204    fmt.Printf("Hit Rate: %.2f%%\n", hitRate)
205}

Example 2: Real-Time Analytics System

  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "sync"
  7    "time"
  8)
  9
 10type Event struct {
 11    Type      string
 12    Timestamp time.Time
 13    UserID    string
 14    Value     float64
 15    Metadata  map[string]interface{}
 16}
 17
 18type Analytics struct {
 19    // Event counters by type
 20    eventCounts map[string]int64
 21
 22    // User activity tracking
 23    userActivity map[string]time.Time
 24
 25    // Time-based aggregations
 26    hourlyStats map[string]map[string]int64
 27
 28    // Value aggregations
 29    valueStats map[string]ValueAggregate
 30
 31    mu sync.RWMutex
 32}
 33
 34type ValueAggregate struct {
 35    Count   int64
 36    Sum     float64
 37    Min     float64
 38    Max     float64
 39}
 40
 41func NewAnalytics() *Analytics {
 42    return &Analytics{
 43        eventCounts:  make(map[string]int64),
 44        userActivity: make(map[string]time.Time),
 45        hourlyStats:  make(map[string]map[string]int64),
 46        valueStats:   make(map[string]ValueAggregate),
 47    }
 48}
 49
 50func (a *Analytics) TrackEvent(event Event) {
 51    a.mu.Lock()
 52    defer a.mu.Unlock()
 53
 54    // Count event types
 55    a.eventCounts[event.Type]++
 56
 57    // Track user activity
 58    a.userActivity[event.UserID] = event.Timestamp
 59
 60    // Hourly statistics
 61    hourKey := event.Timestamp.Format("2006-01-02-15")
 62    if a.hourlyStats[hourKey] == nil {
 63        a.hourlyStats[hourKey] = make(map[string]int64)
 64    }
 65    a.hourlyStats[hourKey][event.Type]++
 66
 67    // Value aggregations
 68    if event.Value > 0 {
 69        agg := a.valueStats[event.Type]
 70        agg.Count++
 71        agg.Sum += event.Value
 72
 73        if agg.Count == 1 || event.Value < agg.Min {
 74            agg.Min = event.Value
 75        }
 76        if agg.Count == 1 || event.Value > agg.Max {
 77            agg.Max = event.Value
 78        }
 79
 80        a.valueStats[event.Type] = agg
 81    }
 82}
 83
 84func (a *Analytics) GetEventCounts() map[string]int64 {
 85    a.mu.RLock()
 86    defer a.mu.RUnlock()
 87
 88    result := make(map[string]int64)
 89    for k, v := range a.eventCounts {
 90        result[k] = v
 91    }
 92    return result
 93}
 94
 95func (a *Analytics) GetActiveUsers(since time.Time) []string {
 96    a.mu.RLock()
 97    defer a.mu.RUnlock()
 98
 99    var activeUsers []string
100    for userID, lastSeen := range a.userActivity {
101        if lastSeen.After(since) {
102            activeUsers = append(activeUsers, userID)
103        }
104    }
105    return activeUsers
106}
107
108func (a *Analytics) GetHourlyStats() map[string]map[string]int64 {
109    a.mu.RLock()
110    defer a.mu.RUnlock()
111
112    result := make(map[string]map[string]int64)
113    for hour, events := range a.hourlyStats {
114        result[hour] = make(map[string]int64)
115        for eventType, count := range events {
116            result[hour][eventType] = count
117        }
118    }
119    return result
120}
121
122func (a *Analytics) GetValueStats(eventType string) (ValueAggregate, bool) {
123    a.mu.RLock()
124    defer a.mu.RUnlock()
125
126    agg, exists := a.valueStats[eventType]
127    return agg, exists
128}
129
130func (a *Analytics) GenerateReport() {
131    a.mu.RLock()
132    defer a.mu.RUnlock()
133
134    fmt.Println("=== Analytics Report ===")
135
136    // Event type summary
137    fmt.Println("\nEvent Summary:")
138    for eventType, count := range a.eventCounts {
139        fmt.Printf("  %s: %d events\n", eventType, count)
140    }
141
142    // Active users
143    oneHourAgo := time.Now().Add(-time.Hour)
144    activeUsers := 0
145    for _, lastSeen := range a.userActivity {
146        if lastSeen.After(oneHourAgo) {
147            activeUsers++
148        }
149    }
150    fmt.Printf("\nActive Users (last hour): %d\n", activeUsers)
151
152    // Value statistics
153    fmt.Println("\nValue Statistics:")
154    for eventType, agg := range a.valueStats {
155        fmt.Printf("  %s:\n", eventType)
156        fmt.Printf("    Count: %d\n", agg.Count)
157        fmt.Printf("    Average: %.2f\n", agg.Sum/float64(agg.Count))
158        fmt.Printf("    Range: %.2f - %.2f\n", agg.Min, agg.Max)
159    }
160
161    // Recent hourly activity
162    fmt.Println("\nRecent Hourly Activity:")
163    now := time.Now()
164    for i := 0; i < 3; i++ {
165        hourKey := now.Add(time.Duration(-i) * time.Hour).Format("2006-01-02-15")
166        if hourStats, exists := a.hourlyStats[hourKey]; exists {
167            total := 0
168            for _, count := range hourStats {
169                total += int(count)
170            }
171            fmt.Printf("  %s: %d events\n", hourKey, total)
172        }
173    }
174}
175
176func main() {
177    analytics := NewAnalytics()
178
179    fmt.Println("=== Real-Time Analytics System ===")
180
181    // Simulate some events
182    events := []Event{
183        {"login", time.Now().Add(-2 * time.Hour), "user1", 0, map[string]interface{}{"ip": "192.168.1.1"}},
184        {"page_view", time.Now().Add(-90 * time.Minute), "user1", 0, map[string]interface{}{"page": "/home"}},
185        {"purchase", time.Now().Add(-85 * time.Minute), "user1", 29.99, map[string]interface{}{"product": "book"}},
186        {"login", time.Now().Add(-60 * time.Minute), "user2", 0, map[string]interface{}{"ip": "192.168.1.2"}},
187        {"page_view", time.Now().Add(-45 * time.Minute), "user2", 0, map[string]interface{}{"page": "/products"}},
188        {"purchase", time.Now().Add(-30 * time.Minute), "user2", 49.99, map[string]interface{}{"product": "electronics"}},
189        {"login", time.Now().Add(-15 * time.Minute), "user3", 0, map[string]interface{}{"ip": "192.168.1.3"}},
190        {"page_view", time.Now().Add(-10 * time.Minute), "user3", 0, map[string]interface{}{"page": "/about"}},
191        {"click", time.Now().Add(-5 * time.Minute), "user3", 0, map[string]interface{}{"element": "button"}},
192    }
193
194    // Track events
195    for _, event := range events {
196        analytics.TrackEvent(event)
197    }
198
199    // Generate report
200    analytics.GenerateReport()
201
202    // Additional queries
203    fmt.Println("\n=== Additional Queries ===")
204
205    eventCounts := analytics.GetEventCounts()
206    fmt.Printf("Total login events: %d\n", eventCounts["login"])
207    fmt.Printf("Total purchase events: %d\n", eventCounts["purchase"])
208
209    activeUsers := analytics.GetActiveUsers(time.Now().Add(-time.Hour))
210    fmt.Printf("Active users: %v\n", activeUsers)
211
212    if purchaseStats, exists := analytics.GetValueStats("purchase"); exists {
213        fmt.Printf("Purchase statistics: avg=$%.2f, range=$%.2f-$%.2f\n",
214            purchaseStats.Sum/float64(purchaseStats.Count),
215            purchaseStats.Min,
216            purchaseStats.Max)
217    }
218}

Performance Considerations

Map Growth and Memory

Understanding how maps grow is crucial for performance optimization:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "runtime"
 7    "time"
 8)
 9
10func mapGrowthDemo() {
11    fmt.Println("=== Map Growth Analysis ===")
12
13    // Pre-allocated map
14    prealloc := make(map[int]string, 10000)
15    start := time.Now()
16    for i := 0; i < 10000; i++ {
17        prealloc[i] = fmt.Sprintf("value%d", i)
18    }
19    preallocTime := time.Since(start)
20
21    // Growing map
22    growing := make(map[int]string)
23    start = time.Now()
24    for i := 0; i < 10000; i++ {
25        growing[i] = fmt.Sprintf("value%d", i)
26    }
27    growingTime := time.Since(start)
28
29    fmt.Printf("Pre-allocated: %v\n", preallocTime)
30    fmt.Printf("Growing: %v\n", growingTime)
31    fmt.Printf("Speedup: %.2fx\n", float64(growingTime.Nanoseconds())/float64(preallocTime.Nanoseconds()))
32
33    // Memory usage
34    runtime.GC()
35    var m1, m2 runtime.MemStats
36    runtime.ReadMemStats(&m1)
37
38    prealloc = make(map[int]string, 10000)
39    for i := 0; i < 10000; i++ {
40        prealloc[i] = fmt.Sprintf("value%d", i)
41    }
42
43    runtime.ReadMemStats(&m2)
44    fmt.Printf("Memory for pre-allocated map: %d KB\n", (m2.Alloc-m1.Alloc)/1024)
45}
46
47func mapPerformanceComparison() {
48    fmt.Println("\n=== Map Performance Comparison ===")
49
50    size := 100000
51    testMap := make(map[string]int, size)
52
53    // Initialize test data
54    for i := 0; i < size; i++ {
55        key := fmt.Sprintf("key_%d", i)
56        testMap[key] = i
57    }
58
59    // Test access performance
60    start := time.Now()
61    sum := 0
62    for i := 0; i < size; i++ {
63        key := fmt.Sprintf("key_%d", i)
64        sum += testMap[key]
65    }
66    accessTime := time.Since(start)
67    fmt.Printf("Sequential access: %v (sum: %d)\n", accessTime, sum)
68
69    // Test range performance
70    start = time.Now()
71    count := 0
72    for _, value := range testMap {
73        count += value
74    }
75    rangeTime := time.Since(start)
76    fmt.Printf("Range iteration: %v (sum: %d)\n", rangeTime, count)
77
78    // Test deletion performance
79    start = time.Now()
80    for i := 0; i < size/2; i++ {
81        key := fmt.Sprintf("key_%d", i)
82        delete(testMap, key)
83    }
84    deleteTime := time.Since(start)
85    fmt.Printf("Half deletion: %v\n", deleteTime)
86    fmt.Printf("Remaining items: %d\n", len(testMap))
87}
88
89func main() {
90    mapGrowthDemo()
91    mapPerformanceComparison()
92}

Key Type Selection

Different key types have different performance characteristics:

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7)
 8
 9type UserKey int
10type ProductKey string
11
12type ComplexKey struct {
13    Type   string
14    ID     int
15    Region string
16}
17
18func keyTypeComparison() {
19    fmt.Println("=== Key Type Performance ===")
20
21    iterations := 100000
22
23    // Integer keys
24    intMap := make(map[int]string, iterations)
25    start := time.Now()
26    for i := 0; i < iterations; i++ {
27        intMap[i] = fmt.Sprintf("value%d", i)
28    }
29    intTime := time.Since(start)
30
31    // String keys
32    strMap := make(map[string]string, iterations)
33    start = time.Now()
34    for i := 0; i < iterations; i++ {
35        key := fmt.Sprintf("key_%d", i)
36        strMap[key] = fmt.Sprintf("value%d", i)
37    }
38    strTime := time.Since(start)
39
40    // Struct keys
41    structMap := make(map[ComplexKey]string, iterations/10)
42    start = time.Now()
43    for i := 0; i < iterations/10; i++ {
44        key := ComplexKey{Type: "user", ID: i, Region: "us-east"}
45        structMap[key] = fmt.Sprintf("value%d", i)
46    }
47    structTime := time.Since(start)
48
49    fmt.Printf("Integer keys: %v\n", intTime)
50    fmt.Printf("String keys: %v\n", strTime)
51    fmt.Printf("Struct keys: %v\n", structTime)
52
53    // Performance comparison
54    fmt.Printf("\nPerformance ratios:\n")
55    fmt.Printf("String vs Int: %.2fx slower\n", float64(strTime.Nanoseconds())/float64(intTime.Nanoseconds()))
56    fmt.Printf("Struct vs Int: %.2fx slower\n", float64(structTime.Nanoseconds())/float64(intTime.Nanoseconds()))
57}
58
59func main() {
60    keyTypeComparison()
61}

Map vs Slice Performance Trade-offs

 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "time"
 7)
 8
 9type Item struct {
10    ID   int
11    Name string
12}
13
14func main() {
15    fmt.Println("=== Map vs Slice Performance ===")
16
17    size := 10000
18
19    // Build test data
20    items := make([]Item, size)
21    for i := 0; i < size; i++ {
22        items[i] = Item{ID: i, Name: fmt.Sprintf("Item %d", i)}
23    }
24
25    // Map lookup performance
26    itemMap := make(map[int]Item, size)
27    for _, item := range items {
28        itemMap[item.ID] = item
29    }
30
31    start := time.Now()
32    for i := 0; i < 1000; i++ {
33        _ = itemMap[i*10]
34    }
35    mapTime := time.Since(start)
36
37    // Slice linear search performance
38    start = time.Now()
39    for i := 0; i < 1000; i++ {
40        target := i * 10
41        for _, item := range items {
42            if item.ID == target {
43                _ = item
44                break
45            }
46        }
47    }
48    sliceTime := time.Since(start)
49
50    fmt.Printf("Map lookups:   %v\n", mapTime)
51    fmt.Printf("Slice lookups: %v\n", sliceTime)
52    fmt.Printf("Map is %.1fx faster\n",
53        float64(sliceTime.Nanoseconds())/float64(mapTime.Nanoseconds()))
54}

Practice Exercises

Exercise 1: Word Frequency Counter

🎯 Learning Objectives: Master map iteration, key-value operations, and frequency counting algorithms.

🌍 Real-World Context: Word frequency analysis is fundamental to text processing, search engines, and natural language processing. Search engines use it for indexing, social media platforms use it for trending topics, and content analysis tools use it for keyword extraction.

⭐ Difficulty: Beginner | ⏱️ Time Estimate: 15 minutes

Write a program that counts the frequency of each word in a given text.

Solution
 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "strings"
 7)
 8
 9func wordFrequency(text string) map[string]int {
10    words := strings.Fields(strings.ToLower(text))
11    freq := make(map[string]int)
12
13    for _, word := range words {
14        freq[word]++
15    }
16
17    return freq
18}
19
20func main() {
21    text := "Go is an open source programming language that makes it easy to build simple reliable and efficient software"
22
23    freq := wordFrequency(text)
24
25    fmt.Println("Word frequency:")
26    for word, count := range freq {
27        fmt.Printf("%s: %d\n", word, count)
28    }
29}

Exercise 2: Anagram Detector

🎯 Learning Objectives: Practice map-based character counting and string manipulation algorithms.

🌍 Real-World Context: Anagram detection is used in word games, cryptography, and text analysis. Spell checkers use it to suggest corrections, security systems use it in password strength validation, and puzzle games use it extensively.

⭐ Difficulty: Beginner | ⏱️ Time Estimate: 12 minutes

Write a function that determines if two strings are anagrams of each other.

Solution
 1// run
 2package main
 3
 4import (
 5    "fmt"
 6    "sort"
 7)
 8
 9// Method 1: Using character frequency maps
10func areAnagramsMap(s1, s2 string) bool {
11    if len(s1) != len(s2) {
12        return false
13    }
14
15    freq := make(map[rune]int)
16
17    // Count characters in first string
18    for _, char := range s1 {
19        freq[char]++
20    }
21
22    // Subtract counts using second string
23    for _, char := range s2 {
24        freq[char]--
25        if freq[char] < 0 {
26            return false
27        }
28    }
29
30    return true
31}
32
33// Method 2: Using sorting
34func areAnagramsSort(s1, s2 string) bool {
35    if len(s1) != len(s2) {
36        return false
37    }
38
39    runes1 := []rune(s1)
40    runes2 := []rune(s2)
41
42    sort.Slice(runes1, func(i, j int) bool {
43        return runes1[i] < runes1[j]
44    })
45
46    sort.Slice(runes2, func(i, j int) bool {
47        return runes2[i] < runes2[j]
48    })
49
50    return string(runes1) == string(runes2)
51}
52
53func main() {
54    tests := []struct {
55        s1, s2 string
56        expected bool
57    }{
58        {"listen", "silent", true},
59        {"hello", "world", false},
60        {"triangle", "integral", true},
61        {"apple", "papel", true},
62        {"test", "sett", true},
63    }
64
65    fmt.Println("Anagram detection:")
66    for _, test := range tests {
67        result1 := areAnagramsMap(test.s1, test.s2)
68        result2 := areAnagramsSort(test.s1, test.s2)
69
70        fmt.Printf("%s + %s: map=%t, sort=%t - expected: %t\n",
71            test.s1, test.s2, result1, result2, test.expected)
72    }
73}

Exercise 3: Map Merging

🎯 Learning Objectives: Implement map combination operations and understand map copying semantics.

🌍 Real-World Context: Map merging is essential for configuration management, data aggregation, and API response handling. Configuration systems merge default and user settings, analytics systems merge data from multiple sources, and database queries often require result merging.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 18 minutes

Write a function that merges two maps, with the second map's values taking precedence.

Solution
 1// run
 2package main
 3
 4import "fmt"
 5
 6func mergeMaps[K comparable, V any](map1, map2 map[K]V) map[K]V {
 7    result := make(map[K]V, len(map1)+len(map2))
 8
 9    // Copy first map
10    for key, value := range map1 {
11        result[key] = value
12    }
13
14    // Copy second map (overwriting conflicts)
15    for key, value := range map2 {
16        result[key] = value
17    }
18
19    return result
20}
21
22func mergeMapsAdd[K comparable](map1, map2 map[K]int) map[K]int {
23    result := make(map[K]int, len(map1)+len(map2))
24
25    // Copy first map
26    for key, value := range map1 {
27        result[key] = value
28    }
29
30    // Add values from second map
31    for key, value := range map2 {
32        result[key] += value
33    }
34
35    return result
36}
37
38func main() {
39    // Test basic merging
40    defaults := map[string]int{
41        "timeout": 30,
42        "retries": 3,
43        "buffer":  1024,
44    }
45
46    userConfig := map[string]int{
47        "timeout": 60,  // Override default
48        "threads": 8,  // New value
49    }
50
51    merged := mergeMaps(defaults, userConfig)
52    fmt.Println("Basic merge:")
53    for key, value := range merged {
54        fmt.Printf("  %s: %d\n", key, value)
55    }
56
57    // Test addition merging
58    sales1 := map[string]int{
59        "jan": 1000,
60        "feb": 1200,
61        "mar": 1100,
62    }
63
64    sales2 := map[string]int{
65        "mar": 200,   // Additional for March
66        "apr": 1300,
67        "may": 1400,
68    }
69
70    totalSales := mergeMapsAdd(sales1, sales2)
71    fmt.Println("\nSales aggregation:")
72    for month, total := range totalSales {
73        fmt.Printf("  %s: %d\n", month, total)
74    }
75}

Exercise 4: Cache Implementation

🎯 Learning Objectives: Build a simple cache system using maps and understand caching patterns.

🌍 Real-World Context: Caching is fundamental to performance optimization. Web applications cache database results, file systems cache disk access, and APIs cache external service responses. Proper caching can reduce response times by orders of magnitude.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 25 minutes

Implement a simple LRU cache using maps.

Solution
  1// run
  2package main
  3
  4import (
  5    "container/list"
  6    "fmt"
  7    "sync"
  8    "time"
  9)
 10
 11type CacheItem struct {
 12    key   string
 13    value interface{}
 14}
 15
 16type LRUCache struct {
 17    capacity int
 18    cache    map[string]*list.Element
 19    list     *list.List
 20    mu       sync.RWMutex
 21}
 22
 23func NewLRUCache(capacity int) *LRUCache {
 24    return &LRUCache{
 25        capacity: capacity,
 26        cache:    make(map[string]*list.Element),
 27        list:     list.New(),
 28    }
 29}
 30
 31func (c *LRUCache) Get(key string) (interface{}, bool) {
 32    c.mu.Lock()
 33    defer c.mu.Unlock()
 34
 35    if element, exists := c.cache[key]; exists {
 36        // Move to front
 37        c.list.MoveToFront(element)
 38        return element.Value.(*CacheItem).value, true
 39    }
 40
 41    return nil, false
 42}
 43
 44func (c *LRUCache) Put(key string, value interface{}) {
 45    c.mu.Lock()
 46    defer c.mu.Unlock()
 47
 48    if element, exists := c.cache[key]; exists {
 49        // Update existing item
 50        c.list.MoveToFront(element)
 51        element.Value.(*CacheItem).value = value
 52        return
 53    }
 54
 55    // Add new item
 56    item := &CacheItem{key: key, value: value}
 57    element := c.list.PushFront(item)
 58    c.cache[key] = element
 59
 60    // Remove least recently used if at capacity
 61    if c.list.Len() > c.capacity {
 62        last := c.list.Back()
 63        if last != nil {
 64            c.list.Remove(last)
 65            delete(c.cache, last.Value.(*CacheItem).key)
 66        }
 67    }
 68}
 69
 70func (c *LRUCache) Size() int {
 71    c.mu.RLock()
 72    defer c.mu.RUnlock()
 73    return c.list.Len()
 74}
 75
 76func (c *LRUCache) Clear() {
 77    c.mu.Lock()
 78    defer c.mu.Unlock()
 79
 80    c.cache = make(map[string]*list.Element)
 81    c.list = list.New()
 82}
 83
 84func main() {
 85    cache := NewLRUCache(3) // Capacity of 3 items
 86
 87    fmt.Println("=== LRU Cache Demo ===")
 88
 89    // Add items
 90    cache.Put("a", "Value A")
 91    cache.Put("b", "Value B")
 92    cache.Put("c", "Value C")
 93
 94    fmt.Printf("Cache size after 3 items: %d\n", cache.Size())
 95
 96    // Access item 'a'
 97    if value, found := cache.Get("a"); found {
 98        fmt.Printf("Found 'a': %v\n", value)
 99    }
100
101    // Add item 'd' (should evict 'b')
102    cache.Put("d", "Value D")
103    fmt.Printf("Cache size after adding 'd': %d\n", cache.Size())
104
105    // Check what's in cache
106    items := []string{"a", "b", "c", "d"}
107    for _, key := range items {
108        if value, found := cache.Get(key); found {
109            fmt.Printf("Found '%s': %v\n", key, value)
110        } else {
111            fmt.Printf("Not found '%s'\n", key)
112        }
113    }
114
115    // Demonstrate cache in concurrent scenario
116    fmt.Println("\n=== Concurrent Access Demo ===")
117    var wg sync.WaitGroup
118
119    // Multiple goroutines accessing cache
120    for i := 0; i < 5; i++ {
121        wg.Add(1)
122        go func(id int) {
123            defer wg.Done()
124            key := fmt.Sprintf("key_%d", id)
125            value := fmt.Sprintf("value_%d", id)
126
127            cache.Put(key, value)
128            time.Sleep(time.Millisecond * 10)
129
130            if retrieved, found := cache.Get(key); found {
131                fmt.Printf("Goroutine %d: retrieved %v\n", id, retrieved)
132            }
133        }(i)
134    }
135
136    wg.Wait()
137    fmt.Printf("Final cache size: %d\n", cache.Size())
138}

Exercise 5: Grouping and Aggregation

🎯 Learning Objectives: Master map-based data grouping and aggregation patterns for data analysis.

🌍 Real-World Context: Data grouping and aggregation are essential for analytics, reporting, and business intelligence. E-commerce platforms group sales by category and time, social media platforms group posts by user and topic, and financial applications group transactions by type and date.

⭐ Difficulty: Intermediate | ⏱️ Time Estimate: 20 minutes

Create a system that groups data by multiple criteria and performs aggregations.

Solution
  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "strings"
  7    "time"
  8)
  9
 10type Transaction struct {
 11    ID        string
 12    Amount    float64
 13    Category  string
 14    Date      time.Time
 15    UserID    string
 16    Payment   string
 17}
 18
 19type TransactionStats struct {
 20    Count    int
 21    Total    float64
 22    Average  float64
 23    Min      float64
 24    Max      float64
 25}
 26
 27func analyzeTransactions(transactions []Transaction) {
 28    // Group by category
 29    byCategory := make(map[string][]Transaction)
 30    for _, tx := range transactions {
 31        byCategory[tx.Category] = append(byCategory[tx.Category], tx)
 32    }
 33
 34    // Group by user
 35    byUser := make(map[string][]Transaction)
 36    for _, tx := range transactions {
 37        byUser[tx.UserID] = append(byUser[tx.UserID], tx)
 38    }
 39
 40    // Group by payment method
 41    byPayment := make(map[string][]Transaction)
 42    for _, tx := range transactions {
 43        byPayment[tx.Payment] = append(byPayment[tx.Payment], tx)
 44    }
 45
 46    // Group by month
 47    byMonth := make(map[string][]Transaction)
 48    for _, tx := range transactions {
 49        month := tx.Date.Format("2006-01")
 50        byMonth[month] = append(byMonth[month], tx)
 51    }
 52
 53    fmt.Println("=== Transaction Analysis ===")
 54
 55    // Category summary
 56    fmt.Println("\nBy Category:")
 57    for category, txs := range byCategory {
 58        stats := calculateStats(txs)
 59        fmt.Printf("%s: %d transactions, $%.2f total, avg: $%.2f\n",
 60            category, stats.Count, stats.Total, stats.Average)
 61    }
 62
 63    // User summary
 64    fmt.Println("\nBy User:")
 65    for userID, txs := range byUser {
 66        stats := calculateStats(txs)
 67        fmt.Printf("%s: %d transactions, $%.2f total, avg: $%.2f\n",
 68            userID, stats.Count, stats.Total, stats.Average)
 69    }
 70
 71    // Payment method summary
 72    fmt.Println("\nBy Payment Method:")
 73    for payment, txs := range byPayment {
 74        stats := calculateStats(txs)
 75        fmt.Printf("%s: %d transactions, $%.2f total, avg: $%.2f\n",
 76            payment, stats.Count, stats.Total, stats.Average)
 77    }
 78
 79    // Monthly trends
 80    fmt.Println("\nBy Month:")
 81    for month, txs := range byMonth {
 82        stats := calculateStats(txs)
 83        fmt.Printf("%s: %d transactions, $%.2f total, avg: $%.2f\n",
 84            month, stats.Count, stats.Total, stats.Average)
 85    }
 86}
 87
 88func calculateStats(transactions []Transaction) TransactionStats {
 89    if len(transactions) == 0 {
 90        return TransactionStats{}
 91    }
 92
 93    stats := TransactionStats{
 94        Count: len(transactions),
 95        Min:   transactions[0].Amount,
 96        Max:   transactions[0].Amount,
 97    }
 98
 99    for _, tx := range transactions {
100        stats.Total += tx.Amount
101        if tx.Amount < stats.Min {
102            stats.Min = tx.Amount
103        }
104        if tx.Amount > stats.Max {
105            stats.Max = tx.Amount
106        }
107    }
108
109    stats.Average = stats.Total / float64(stats.Count)
110    return stats
111}
112
113func main() {
114    // Sample transaction data
115    transactions := []Transaction{
116        {"1", 29.99, "Electronics", time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC), "user1", "credit"},
117        {"2", 15.50, "Books", time.Date(2024, 1, 16, 0, 0, 0, 0, time.UTC), "user1", "debit"},
118        {"3", 199.99, "Electronics", time.Date(2024, 1, 18, 0, 0, 0, 0, time.UTC), "user2", "credit"},
119        {"4", 45.00, "Clothing", time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC), "user2", "paypal"},
120        {"5", 89.99, "Electronics", time.Date(2024, 2, 5, 0, 0, 0, 0, time.UTC), "user1", "credit"},
121        {"6", 25.00, "Food", time.Date(2024, 2, 8, 0, 0, 0, 0, time.UTC), "user3", "debit"},
122        {"7", 350.00, "Electronics", time.Date(2024, 2, 10, 0, 0, 0, 0, time.UTC), "user3", "credit"},
123        {"8", 12.99, "Books", time.Date(2024, 2, 12, 0, 0, 0, 0, time.UTC), "user2", "debit"},
124    }
125
126    analyzeTransactions(transactions)
127}

Exercise 6: Map as Data Index

🎯 Learning Objectives: Implement multi-field indexing using maps for efficient data retrieval.

🌍 Real-World Context: Data indexing is crucial for search functionality and database operations. E-commerce sites index products by category, brand, and price range. Social media platforms index content by hashtags, users, and engagement metrics. Search engines build complex indexes for fast retrieval.

⭐ Difficulty: Advanced | ⏱️ Time Estimate: 30 minutes

Create an indexing system for products that allows fast lookup by multiple criteria.

Solution
  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "strings"
  7    "sync"
  8)
  9
 10type Product struct {
 11    ID          string
 12    Name        string
 13    Category    string
 14    Price       float64
 15    Brand       string
 16    Tags        []string
 17    InStock     bool
 18    Rating      float64
 19}
 20
 21type ProductIndex struct {
 22    // Primary storage
 23    products map[string]Product
 24
 25    // Secondary indexes
 26    byCategory map[string][]string  // category -> product IDs
 27    byBrand    map[string][]string  // brand -> product IDs
 28    byTag      map[string][]string  // tag -> product IDs
 29    byPrice    map[string][]string  // price range -> product IDs
 30
 31    mu sync.RWMutex
 32}
 33
 34func NewProductIndex() *ProductIndex {
 35    return &ProductIndex{
 36        products:   make(map[string]Product),
 37        byCategory: make(map[string][]string),
 38        byBrand:    make(map[string][]string),
 39        byTag:      make(map[string][]string),
 40        byPrice:    make(map[string][]string),
 41    }
 42}
 43
 44func (pi *ProductIndex) AddProduct(product Product) {
 45    pi.mu.Lock()
 46    defer pi.mu.Unlock()
 47
 48    // Add to primary storage
 49    pi.products[product.ID] = product
 50
 51    // Update secondary indexes
 52    pi.byCategory[product.Category] = append(pi.byCategory[product.Category], product.ID)
 53    pi.byBrand[product.Brand] = append(pi.byBrand[product.Brand], product.ID)
 54
 55    for _, tag := range product.Tags {
 56        pi.byTag[tag] = append(pi.byTag[tag], product.ID)
 57    }
 58
 59    priceRange := pi.getPriceRange(product.Price)
 60    pi.byPrice[priceRange] = append(pi.byPrice[priceRange], product.ID)
 61}
 62
 63func (pi *ProductIndex) getPriceRange(price float64) string {
 64    switch {
 65    case price < 20:
 66        return "0-20"
 67    case price < 50:
 68        return "20-50"
 69    case price < 100:
 70        return "50-100"
 71    case price < 200:
 72        return "100-200"
 73    default:
 74        return "200+"
 75    }
 76}
 77
 78func (pi *ProductIndex) GetByID(id string) (Product, bool) {
 79    pi.mu.RLock()
 80    defer pi.mu.RUnlock()
 81
 82    product, exists := pi.products[id]
 83    return product, exists
 84}
 85
 86func (pi *ProductIndex) FindByCategory(category string) []Product {
 87    pi.mu.RLock()
 88    defer pi.mu.RUnlock()
 89
 90    var products []Product
 91    for _, id := range pi.byCategory[category] {
 92        if product, exists := pi.products[id]; exists {
 93            products = append(products, product)
 94        }
 95    }
 96    return products
 97}
 98
 99func (pi *ProductIndex) FindByBrand(brand string) []Product {
100    pi.mu.RLock()
101    defer pi.mu.RUnlock()
102
103    var products []Product
104    for _, id := range pi.byBrand[brand] {
105        if product, exists := pi.products[id]; exists {
106            products = append(products, product)
107        }
108    }
109    return products
110}
111
112func (pi *ProductIndex) FindByTag(tag string) []Product {
113    pi.mu.RLock()
114    defer pi.mu.RUnlock()
115
116    var products []Product
117    for _, id := range pi.byTag[tag] {
118        if product, exists := pi.products[id]; exists {
119            products = append(products, product)
120        }
121    }
122    return products
123}
124
125func (pi *ProductIndex) FindByPriceRange(minPrice, maxPrice float64) []Product {
126    pi.mu.RLock()
127    defer pi.mu.RUnlock()
128
129    var products []Product
130    for _, product := range pi.products {
131        if product.Price >= minPrice && product.Price <= maxPrice {
132            products = append(products, product)
133        }
134    }
135    return products
136}
137
138func (pi *ProductIndex) Search(query string) []Product {
139    pi.mu.RLock()
140    defer pi.mu.RUnlock()
141
142    query = strings.ToLower(query)
143    var results []Product
144
145    for _, product := range pi.products {
146        // Simple text search in name and brand
147        if strings.Contains(strings.ToLower(product.Name), query) ||
148           strings.Contains(strings.ToLower(product.Brand), query) {
149            results = append(results, product)
150            continue
151        }
152
153        // Search in tags
154        for _, tag := range product.Tags {
155            if strings.Contains(strings.ToLower(tag), query) {
156                results = append(results, product)
157                break
158            }
159        }
160    }
161
162    return results
163}
164
165func (pi *ProductIndex) GetIndexStats() map[string]int {
166    pi.mu.RLock()
167    defer pi.mu.RUnlock()
168
169    return map[string]int{
170        "total_products": len(pi.products),
171        "categories":     len(pi.byCategory),
172        "brands":         len(pi.byBrand),
173        "tags":          len(pi.byTag),
174    }
175}
176
177func main() {
178    index := NewProductIndex()
179
180    fmt.Println("=== Product Index System ===")
181
182    // Add sample products
183    products := []Product{
184        {"p1", "Laptop Pro", "Electronics", 999.99, "TechCo", []string{"laptop", "computer", "work"}, true, 4.5},
185        {"p2", "Wireless Mouse", "Electronics", 29.99, "TechCo", []string{"mouse", "wireless", "computer"}, true, 4.2},
186        {"p3", "Programming Book", "Books", 45.00, "TechPress", []string{"programming", "go", "software"}, true, 4.8},
187        {"p4", "Running Shoes", "Clothing", 89.99, "SportMax", []string{"running", "shoes", "fitness"}, true, 4.1},
188        {"p5", "Smart Watch", "Electronics", 199.99, "TechCo", []string{"watch", "smart", "fitness"}, false, 3.9},
189        {"p6", "Coffee Maker", "Kitchen", 79.99, "BrewMaster", []string{"coffee", "kitchen", "appliance"}, true, 4.3},
190    }
191
192    for _, product := range products {
193        index.AddProduct(product)
194    }
195
196    // Test different search methods
197    fmt.Println("\n=== Search Examples ===")
198
199    // Search by category
200    electronics := index.FindByCategory("Electronics")
201    fmt.Printf("Electronics products (%d):\n", len(electronics))
202    for _, product := range electronics {
203        fmt.Printf("  - %s ($%.2f)\n", product.Name, product.Price)
204    }
205
206    // Search by brand
207    techco := index.FindByBrand("TechCo")
208    fmt.Printf("\nTechCo products (%d):\n", len(techco))
209    for _, product := range techco {
210        fmt.Printf("  - %s (%s)\n", product.Name, product.Category)
211    }
212
213    // Search by tag
214    fitness := index.FindByTag("fitness")
215    fmt.Printf("\nFitness products (%d):\n", len(fitness))
216    for _, product := range fitness {
217        fmt.Printf("  - %s\n", product.Name)
218    }
219
220    // Search by price range
221    affordable := index.FindByPriceRange(0, 100)
222    fmt.Printf("\nAffordable products (%d):\n", len(affordable))
223    for _, product := range affordable {
224        fmt.Printf("  - %s ($%.2f)\n", product.Name, product.Price)
225    }
226
227    // Text search
228    searchResults := index.Search("laptop")
229    fmt.Printf("\nSearch results for 'laptop' (%d):\n", len(searchResults))
230    for _, product := range searchResults {
231        fmt.Printf("  - %s - %s (%s)\n", product.Name, product.Brand, strings.Join(product.Tags, ", "))
232    }
233
234    // Index statistics
235    stats := index.GetIndexStats()
236    fmt.Printf("\nIndex Statistics:\n")
237    for metric, value := range stats {
238        fmt.Printf("  %s: %d\n", metric, value)
239    }
240}

Summary

Key Takeaways

  1. Hash Table Implementation: Maps are hash tables with O(1) average operations
  2. Key Requirements: Keys must be comparable (support == operator)
  3. Dynamic Growth: Maps automatically resize to maintain performance
  4. Value Types: Map values can be any type, including functions and pointers
  5. Zero Values: Maps return zero values for missing keys
  6. Concurrency: Regular maps aren't thread-safe, use sync.Map or mutexes
  7. Memory Overhead: Maps use more memory than arrays but provide flexibility
  8. Performance: Pre-allocation and proper key selection improve performance

Best Practices

  • Pre-allocate capacity when you know approximate map size
  • Use simple key types (int, string) for better performance
  • Check existence with two-value assignment for safety
  • Initialize nil maps before use to avoid panics
  • Use sync.Map for simple concurrent access scenarios
  • Use mutex for complex concurrent operations
  • Consider set semantics using empty struct values (struct{}{})
  • Profile map usage to optimize for your specific workload

When to Use Maps vs Other Data Structures

Use Maps When:

  • You need fast key-based lookups (O(1) average)
  • Keys are unique or you need to handle collisions
  • You need dynamic insertion and deletion
  • You're implementing caching, indexing, or lookup tables
  • You need to represent relationships between entities

Use Other Structures When:

  • Ordered iteration is required (use slice with sorting)
  • Memory efficiency is critical and keys are sequential (use slice)
  • You need fixed-size collections (use array)
  • You need thread-safe access without synchronization overhead (use sync.Map for simple cases)

Next Steps

  • Practice: Implement exercises to reinforce concepts
  • Explore: Learn about sync.Map and concurrent patterns
  • Apply: Use map patterns in your own projects
  • Profile: Use Go's profiling tools to optimize map usage
  • Advanced: Study hash functions and collision resolution algorithms

Mastering maps gives you powerful tools for building efficient, scalable Go applications!