← Go EnglishChapter 07 of 13

Maps

## Learning Objectives - Understand Go maps (hash tables) - Create and initialize maps - Add, access, and delete entries - Iterate over maps - Handle missing keys safely ## What is a Map? A map is a hash table implementation - an unordered collection of key-value pairs where keys are unique. ## Declaration ### With make ```go ages := make(map[string]int) ages["Alice"] = 30 ages["Bob"] = 25 ``` ### With Literals ```go ages := map[string]int{ "Alice": 30, "Bob": 25, "Carol": 35, } ``` ### nil Map ```go var ages map[string]int // nil map (cannot add to it) ages = make(map[string]int) // Initialize before use ``` ## Basic Operations ### Add/Update ```go ages := make(map[string]int) ages["Alice"] = 30 // Add ages["Alice"] = 31 // Update ``` ### Access ```go ages := map[string]int{ "Alice": 30, "Bob": 25, } fmt.Println(ages["Alice"]) // 30 fmt.Println(ages["Unknown"]) // 0 (zero value) ``` ### Check Key Exists ```go ages := map[string]int{ "Alice": 30, } value, exists := ages["Alice"] fmt.Println(value, exists) // 30 true value, exists = ages["Bob"] fmt.Println(value, exists) // 0 false ``` ### Delete ```go ages := map[string]int{ "Alice": 30, "Bob": 25, } delete(ages, "Bob") fmt.Println(ages) // map[Alice:30] ``` ### Delete Non-existent Key ```go ages := map[string]int{"Alice": 30} delete(ages, "Bob") // Safe - no error even if key doesn't exist ``` ## Length ```go ages := map[string]int{ "Alice": 30, "Bob": 25, } fmt.Println(len(ages)) // 2 ``` ## Iteration ### Basic Iteration ```go ages := map[string]int{ "Alice": 30, "Bob": 25, "Carol": 35, } for key, value := range ages { fmt.Printf("%s: %d\n", key, value) } ``` ### Order - Map iteration order is **not guaranteed** - Order may differ between iterations ### Key Only ```go for key := range ages { fmt.Println(key) } ``` ### Sorted Keys ```go ages := map[string]int{ "Charlie": 35, "Alice": 30, "Bob": 25, } keys := make([]string, 0, len(ages)) for key := range ages { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { fmt.Printf("%s: %d\n", key, ages[key]) } ``` ## Zero Value ### Reading from nil Map ```go var ages map[string]int // nil fmt.Println(ages["Alice"]) // 0 fmt.Println(len(ages)) // 0 delete(ages, "Alice") // Safe // ages["Alice"] = 30 // PANIC! ``` ### Check Before Write ```go var ages map[string]int if ages == nil { ages = make(map[string]int) } ages["Alice"] = 30 // Now safe ``` ## Map as Reference Maps are reference types - copying a map shares the underlying data. ```go ages1 := map[string]int{"Alice": 30} ages2 := ages1 ages2["Alice"] = 31 fmt.Println(ages1["Alice"]) // 31 (both reference same map) fmt.Println(ages2["Alice"]) // 31 ``` ## Pointers to Maps ```go func modify(m map[string]int) { m["Alice"] = 31 } ages := map[string]int{"Alice": 30} modify(ages) fmt.Println(ages["Alice"]) // 31 ``` ## Common Patterns ### Word Count ```go text := "hello world hello go programming hello" words := strings.Fields(text) count := make(map[string]int) for _, word := range words { count[word]++ } fmt.Println(count) // map[hello:3 world:1 go:1 programming:1] ``` ### Set Implementation ```go type Set struct { items map[string]struct{} } func NewSet() *Set { return &Set{make(map[string]struct{})} } func (s *Set) Add(item string) { s.items[item] = struct{}{} } func (s *Set) Contains(item string) bool { _, exists := s.items[item] return exists } func (s *Set) Remove(item string) { delete(s.items, item) } func main() { set := NewSet() set.Add("apple") set.Add("banana") fmt.Println(set.Contains("apple")) // true fmt.Println(set.Contains("orange")) // false } ``` ### Group By ```go people := []struct { Name string Age int }{ {"Alice", 30}, {"Bob", 25}, {"Carol", 30}, {"David", 25}, } groups := make(map[int][]string) for _, p := range people { groups[p.Age] = append(groups[p.Age], p.Name) } fmt.Println(groups) // map[25:[Bob David] 30:[Alice Carol]] ``` ### Unique Values ```go func unique(ints []int) []int { seen := make(map[int]bool) result := []int{} for _, n := range ints { if !seen[n] { seen[n] = true result = append(result, n) } } return result } ``` ## Comparison ### Maps Cannot Be Compared ```go m1 := map[string]int{"a": 1} m2 := map[string]int{"a": 1} // m1 == m2 // COMPILE ERROR: map can only be compared to nil ``` ### Deep Compare ```go func equalMaps(m1, m2 map[string]int) bool { if len(m1) != len(m2) { return false } for k, v1 := range m1 { if v2, ok := m2[k]; !ok || v1 != v2 { return false } } return true } ``` ## Concurrent Access ### Race Condition Maps are not safe for concurrent access by default. ```go var counter = make(map[string]int) // This is unsafe! go func() { for i := 0; i < 1000; i++ { counter["a"]++ } }() go func() { for i := 0; i < 1000; i++ { counter["a"]++ } }() ``` ### sync.RWMutex ```go import "sync" var counter = struct { sync.RWMutex m map[string]int }{m: make(map[string]int)} counter.Lock() counter.m["a"]++ counter.Unlock() counter.RLock() fmt.Println(counter.m["a"]) counter.RUnlock() ``` ### sync.Map ```go var syncMap sync.Map syncMap.Store("a", 1) value, ok := syncMap.Load("a") syncMap.Delete("a") syncMap.Range(func(key, value interface{}) bool { fmt.Printf("%s: %d\n", key, value) return true }) ``` ## Summary - Maps are hash tables - key-value pairs with unique keys - Create with `make()` or map literals - Zero value is `nil` - cannot add to nil map - Reading non-existent key returns zero value - Use comma-ok idiom to check key existence - `delete()` is safe even if key doesn't exist - Iteration order is not guaranteed - Maps are reference types - Not safe for concurrent access - use sync.RWMutex or sync.Map

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →