Master Go's powerful map data structure for efficient key-value storage and retrieval. This comprehensive guide covers map creation, operations, iteration patterns, and advanced techniques, building upon our understanding of slices and data types.
Key Points
Maps are reference types with key-value associations
Unordered collections with unique keys
Built-in functions for efficient operations
Comparable key types required
Essential for lookups, caching, and data organization
Understanding Maps in Go
Maps are Go's built-in associative data structure, providing fast key-value lookups similar to hash tables or dictionaries in other languages. They're reference types that offer dynamic sizing and efficient operations.
Map Architecture
graph TD
A[Go Map] --> B[Hash Table]
B --> C[Bucket 0]
B --> D[Bucket 1]
B --> E[Bucket N]
C --> F[Key1: Value1]
C --> G[Key2: Value2]
D --> H[Key3: Value3]
E --> I[...]
style A fill:#999,stroke:#333,stroke-width:2px,color:#000
style B fill:#e1f5fe,stroke:#01579b,stroke-width:2px
Map Creation Patterns
Go provides multiple ways to create and initialize maps, each suited for different scenarios and use cases.
packagemainimport"fmt"funcmain(){// Create map with capacity hint for performancelargeMap:=make(map[int]string,1000)// Add some elementsfori:=0;i<5;i++{largeMap[i]=fmt.Sprintf("value_%d",i)}fmt.Printf("Large map length: %d\n",len(largeMap))// Output: Large map length: 5}
packagemainimport"fmt"funcmain(){// Nil map (zero value)varnilMapmap[string]int// Reading from nil map is safevalue:=nilMap["key"]// Returns zero valuefmt.Printf("Value from nil map: %d\n",value)// Writing to nil map causes panic// nilMap["key"] = 42 // panic: assignment to entry in nil map// Must initialize before writingnilMap=make(map[string]int)nilMap["key"]=42fmt.Printf("After initialization: %v\n",nilMap)}
Map Operations and Access
Maps support efficient operations for adding, updating, accessing, and deleting key-value pairs.
packagemainimport"fmt"funcmain(){inventory:=map[string]int{"apples":50,"bananas":30}// Check if key exists (comma ok idiom)ifcount,exists:=inventory["apples"];exists{fmt.Printf("Apples in stock: %d\n",count)}// Check for non-existent keyifcount,exists:=inventory["oranges"];exists{fmt.Printf("Oranges in stock: %d\n",count)}else{fmt.Println("Oranges not in inventory")}// Direct access returns zero value for missing keysfmt.Printf("Grapes count: %d\n",inventory["grapes"])// 0}
packagemainimport"fmt"funcmain(){grades:=map[string]int{"Alice":95,"Bob":87,"Charlie":92,"Diana":89,}// Iterate over keys and valuesforname,grade:=rangegrades{fmt.Printf("%s: %d\n",name,grade)}// Note: Map iteration order is not guaranteed}
packagemainimport"fmt"funcmain(){scores:=map[string]int{"Alice":95,"Bob":87,"Charlie":92,}// Iterate over values only (using blank identifier)for_,score:=rangescores{fmt.Printf("Score: %d\n",score)}}
packagemainimport"fmt"typePointstruct{X,Yint}funcmain(){// Struct keys (must be comparable)distances:=map[Point]float64{{0,0}:0.0,{1,1}:1.414,{3,4}:5.0,}origin:=Point{0,0}fmt.Printf("Distance from origin: %.3f\n",distances[origin])}
packagemainimport"fmt"funcmain(){// Map with slice values for groupingcategories:=map[string][]string{"fruits":{"apple","banana","orange"},"vegetables":{"carrot","broccoli","spinach"},"grains":{"rice","wheat","oats"},}// Add to existing slicecategories["fruits"]=append(categories["fruits"],"grape")fmt.Printf("Fruits: %v\n",categories["fruits"])}
packagemainimport"fmt"funcmain(){// Nested maps for complex data structuresstudentGrades:=map[string]map[string]int{"Alice":{"Math":95,"Science":87,"English":92},"Bob":{"Math":78,"Science":85,"English":88},}// Access nested valuesaliceMath:=studentGrades["Alice"]["Math"]fmt.Printf("Alice's Math grade: %d\n",aliceMath)// Add new studentstudentGrades["Charlie"]=map[string]int{"Math":90,"Science":93,"English":89,}}
packagemainimport"fmt"funcmain(){words:=[]string{"apple","banana","apple","orange","banana","apple"}// Count word occurrencescounts:=make(map[string]int)for_,word:=rangewords{counts[word]++// Zero value of int is 0}fmt.Printf("Word counts: %v\n",counts)// Output: map[apple:3 banana:2 orange:1]}
packagemainimport"fmt"funcmain(){// Implement set using map[T]booluniqueItems:=make(map[string]bool)items:=[]string{"apple","banana","apple","orange","banana"}// Add items to setfor_,item:=rangeitems{uniqueItems[item]=true}// Check membershipifuniqueItems["apple"]{fmt.Println("Apple is in the set")}// Get all unique itemsfmt.Print("Unique items: ")foritem:=rangeuniqueItems{fmt.Printf("%s ",item)}fmt.Println()}
packagemainimport"fmt"typePersonstruct{NamestringAgeintCitystring}funcmain(){people:=[]Person{{"Alice",25,"NYC"},{"Bob",30,"LA"},{"Charlie",35,"NYC"},}// Group people by citybyCity:=make(map[string][]Person)for_,person:=rangepeople{byCity[person.City]=append(byCity[person.City],person)}forcity,residents:=rangebyCity{fmt.Printf("%s: %d residents\n",city,len(residents))}}
// Use sync.Map or mutex for concurrent accessvarmusync.RWMutex// orvarmsync.Map
Common Pitfalls
Nil Map Writes: Writing to nil map causes panic
Iteration Order: Never rely on map iteration order
Concurrent Access: Maps are not thread-safe
Key Comparability: Only comparable types can be keys
Quick Reference
Key Takeaways
Creation: Use literals, make(), or zero value (nil)
Access: Direct access or comma ok idiom for safety
Modification: Assignment for add/update, delete() for removal
Iteration: Use range with various patterns
Performance: O(1) average time complexity for operations
Concurrency: Use synchronization for multi-goroutine access
Remember
"Maps are Go's associative arrays. Master key-value operations, understand reference semantics, and always check for key existence when needed. When in doubt about concurrency, synchronize access."