1. go
  2. /data structures
  3. /maps

Working with Maps in Go

Maps are Go's built-in associative data type (also known as hashes or dictionaries in other languages). This guide covers everything you need to know about working with maps effectively.

Map Basics

Map Declaration

// Basic map declaration
var scores map[string]int

// Create map with make
scores = make(map[string]int)

// Map literal declaration
colors := map[string]string{
    "red":   "#ff0000",
    "green": "#00ff00",
    "blue":  "#0000ff",
}

// Empty map literal
empty := map[string]int{}

Map Operations

// Adding or updating entries
scores["alice"] = 100
scores["bob"] = 85

// Retrieving values
aliceScore := scores["alice"]

// Checking existence
score, exists := scores["charlie"]
if exists {
    fmt.Printf("Score: %d\n", score)
} else {
    fmt.Println("No score found")
}

// Deleting entries
delete(scores, "bob")

// Map length
count := len(scores)

Map Iteration

// Range over map
for key, value := range scores {
    fmt.Printf("%s: %d\n", key, value)
}

// Iterate over keys only
for key := range scores {
    fmt.Printf("Key: %s\n", key)
}

// Iterate over values only
for _, value := range scores {
    fmt.Printf("Value: %d\n", value)
}

Map Types

Basic Map Types

// String keys (most common)
users := map[string]User{}

// Integer keys
matrix := map[int][]float64{}

// Boolean keys
flags := map[bool]string{
    true:  "yes",
    false: "no",
}

// Struct keys
type Point struct {
    X, Y int
}
locations := map[Point]string{}

Complex Map Types

// Maps of maps
graph := map[string]map[string]int{
    "A": {"B": 1, "C": 2},
    "B": {"A": 1, "D": 3},
}

// Maps of slices
userRoles := map[string][]string{
    "alice": {"admin", "user"},
    "bob":   {"user"},
}

// Maps of interfaces
data := map[string]interface{}{
    "name":    "John",
    "age":     30,
    "scores":  []int{85, 90, 95},
    "address": map[string]string{"city": "New York"},
}

Map Patterns

1. Set Implementation

// Set using map with empty struct
type Set map[string]struct{}

func (s Set) Add(item string) {
    s[item] = struct{}{}
}

func (s Set) Remove(item string) {
    delete(s, item)
}

func (s Set) Contains(item string) bool {
    _, exists := s[item]
    return exists
}

// Usage
uniqueWords := make(Set)
uniqueWords.Add("apple")
uniqueWords.Add("banana")
fmt.Println(uniqueWords.Contains("apple"))  // true

2. Counter Implementation

type Counter map[string]int

func (c Counter) Increment(key string) {
    c[key]++
}

func (c Counter) GetCount(key string) int {
    return c[key]
}

// Usage
wordCount := make(Counter)
words := []string{"apple", "banana", "apple", "cherry"}
for _, word := range words {
    wordCount.Increment(word)
}

3. Cache Implementation

type Cache struct {
    data map[string]interface{}
    mu   sync.RWMutex
}

func NewCache() *Cache {
    return &Cache{
        data: make(map[string]interface{}),
    }
}

func (c *Cache) Set(key string, value interface{}) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.data[key] = value
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    value, exists := c.data[key]
    return value, exists
}

Best Practices

1. Initialization

// Good: Initialize with expected size
users := make(map[string]User, 100)

// Avoid: Growing map without size hint
users := make(map[string]User)  // Default size

// Good: Clear map while preserving capacity
for k := range users {
    delete(users, k)
}

// Avoid: Reallocating map
users = make(map[string]User)  // Loses allocated space

2. Concurrent Access

// Good: Use sync.Map for concurrent access
var cache sync.Map

cache.Store("key", "value")
value, ok := cache.Load("key")

// Alternative: Use mutex
type SafeMap struct {
    sync.RWMutex
    data map[string]interface{}
}

func (m *SafeMap) Get(key string) interface{} {
    m.RLock()
    defer m.RUnlock()
    return m.data[key]
}

3. Memory Management

// Good: Delete unused entries
for key := range largeMap {
    if isExpired(key) {
        delete(largeMap, key)
    }
}

// Good: Use pointers for large values
map[string]*LargeStruct{}  // Instead of map[string]LargeStruct

// Avoid memory leaks
// Clear references in values
for k := range objMap {
    objMap[k].Clear()  // Clear internal references
    delete(objMap, k)
}

Common Operations

1. Map Merging

// Merge two maps
func merge(m1, m2 map[string]int) map[string]int {
    result := make(map[string]int, len(m1)+len(m2))
    
    for k, v := range m1 {
        result[k] = v
    }
    
    for k, v := range m2 {
        result[k] = v  // m2 values override m1
    }
    
    return result
}

2. Map Filtering

// Filter map entries
func filter(m map[string]int, pred func(string, int) bool) map[string]int {
    result := make(map[string]int)
    
    for k, v := range m {
        if pred(k, v) {
            result[k] = v
        }
    }
    
    return result
}

// Usage
positives := filter(numbers, func(k string, v int) bool {
    return v > 0
})

3. Map Transformation

// Transform map values
func transform(m map[string]int, fn func(int) int) map[string]int {
    result := make(map[string]int, len(m))
    
    for k, v := range m {
        result[k] = fn(v)
    }
    
    return result
}

// Usage
doubled := transform(numbers, func(v int) int {
    return v * 2
})

Performance Considerations

1. Map Size

// Pre-allocate for better performance
users := make(map[string]User, expectedSize)

// Benchmark different sizes
func BenchmarkMapOperations(b *testing.B) {
    for size := 100; size <= 10000; size *= 10 {
        b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) {
            m := make(map[string]int, size)
            // Benchmark operations...
        })
    }
}

2. Key Types

// Good: Simple comparable types
map[string]int{}
map[int]string{}

// Avoid: Complex key types
map[*MyStruct]string{}  // Pointer comparison
map[[]int]string{}      // Invalid: slices aren't comparable

3. Value Types

// Good: Use pointers for large values
type LargeStruct struct {
    Data [1024]byte
}
cache := map[string]*LargeStruct{}

// Avoid copying large values
cache := map[string]LargeStruct{}  // Copies on assignment

Next Steps

  1. Learn about structs
  2. Explore interfaces
  3. Study concurrency
  4. Practice with algorithms

Additional Resources