/* 03-maps.go - Go 语言映射详解 学习目标: 1. 理解映射的概念和特性 2. 掌握映射的创建和初始化 3. 学会映射的基本操作 4. 了解映射的内部实现 5. 掌握映射的实际应用场景 知识点: - 映射的定义和特性 - 映射的创建方式 - 映射的增删改查操作 - 映射的遍历 - 映射的零值和比较 - 映射作为集合使用 - 映射的并发安全问题 */ package main import ( "fmt" ) func main() { fmt.Println("=== Go 语言映射详解 ===\n") // 演示映射的基本概念 demonstrateBasicMaps() // 演示映射的创建方式 demonstrateMapCreation() // 演示映射的基本操作 demonstrateMapOperations() // 演示映射的遍历 demonstrateMapIteration() // 演示映射的高级操作 demonstrateAdvancedMapOperations() // 演示映射作为集合使用 demonstrateMapAsSet() // 演示映射的实际应用 demonstratePracticalApplications() } // demonstrateBasicMaps 演示映射的基本概念 func demonstrateBasicMaps() { fmt.Println("1. 映射的基本概念:") // 映射的基本特性 fmt.Printf(" 映射的基本特性:\n") fmt.Printf(" - 键值对的无序集合\n") fmt.Printf(" - 键必须是可比较的类型\n") fmt.Printf(" - 引用类型,零值是 nil\n") fmt.Printf(" - 动态大小,可以增长和缩减\n") fmt.Printf(" - 不能直接比较,只能与 nil 比较\n") // 基本映射示例 fmt.Printf(" 基本映射示例:\n") var m map[string]int // 声明映射,零值为 nil fmt.Printf(" 声明映射: %v\n", m) fmt.Printf(" 是否为 nil: %t\n", m == nil) fmt.Printf(" 长度: %d\n", len(m)) // 初始化映射 scores := map[string]int{ "Alice": 95, "Bob": 87, "Charlie": 92, } fmt.Printf(" 初始化映射: %v\n", scores) fmt.Printf(" 长度: %d\n", len(scores)) // 访问映射元素 fmt.Printf(" Alice的分数: %d\n", scores["Alice"]) fmt.Printf(" 不存在的键: %d\n", scores["David"]) // 返回零值 fmt.Println() } // demonstrateMapCreation 演示映射的创建方式 func demonstrateMapCreation() { fmt.Println("2. 映射的创建方式:") // 方式1: 字面量创建 fmt.Printf(" 方式1 - 字面量创建:\n") colors := map[string]string{ "red": "#FF0000", "green": "#00FF00", "blue": "#0000FF", } fmt.Printf(" colors: %v\n", colors) // 方式2: make 函数创建 fmt.Printf(" 方式2 - make 函数创建:\n") ages := make(map[string]int) ages["Alice"] = 25 ages["Bob"] = 30 fmt.Printf(" ages: %v\n", ages) // 方式3: 空映射字面量 fmt.Printf(" 方式3 - 空映射字面量:\n") empty := map[string]int{} fmt.Printf(" empty: %v (len=%d)\n", empty, len(empty)) fmt.Printf(" 是否为 nil: %t\n", empty == nil) // 方式4: 带容量提示的 make fmt.Printf(" 方式4 - 带容量提示的 make:\n") large := make(map[string]int, 100) // 容量提示,不是限制 fmt.Printf(" large: %v (len=%d)\n", large, len(large)) // 不同类型的映射 fmt.Printf(" 不同类型的映射:\n") // 整数键 intMap := map[int]string{1: "one", 2: "two", 3: "three"} fmt.Printf(" 整数键: %v\n", intMap) // 结构体值 type Person struct { Name string Age int } people := map[string]Person{ "p1": {"Alice", 25}, "p2": {"Bob", 30}, } fmt.Printf(" 结构体值: %v\n", people) // 切片值 groups := map[string][]string{ "fruits": {"apple", "banana", "cherry"}, "vegetables": {"carrot", "broccoli", "spinach"}, } fmt.Printf(" 切片值: %v\n", groups) // 映射值 nested := map[string]map[string]int{ "math": {"Alice": 95, "Bob": 87}, "english": {"Alice": 92, "Bob": 89}, } fmt.Printf(" 嵌套映射: %v\n", nested) fmt.Println() } // demonstrateMapOperations 演示映射的基本操作 func demonstrateMapOperations() { fmt.Println("3. 映射的基本操作:") // 创建映射 inventory := make(map[string]int) // 添加/更新元素 fmt.Printf(" 添加/更新元素:\n") inventory["apples"] = 50 inventory["bananas"] = 30 inventory["oranges"] = 25 fmt.Printf(" 添加水果: %v\n", inventory) // 更新现有元素 inventory["apples"] = 45 fmt.Printf(" 更新苹果数量: %v\n", inventory) // 读取元素 fmt.Printf(" 读取元素:\n") apples := inventory["apples"] fmt.Printf(" 苹果数量: %d\n", apples) // 检查键是否存在 fmt.Printf(" 检查键是否存在:\n") value, exists := inventory["bananas"] fmt.Printf(" bananas存在: %t, 值: %d\n", exists, value) value, exists = inventory["grapes"] fmt.Printf(" grapes存在: %t, 值: %d\n", exists, value) // 删除元素 fmt.Printf(" 删除元素:\n") fmt.Printf(" 删除前: %v\n", inventory) delete(inventory, "oranges") fmt.Printf(" 删除oranges后: %v\n", inventory) // 删除不存在的键(安全操作) delete(inventory, "grapes") fmt.Printf(" 删除不存在的键后: %v\n", inventory) // 批量操作 fmt.Printf(" 批量操作:\n") newItems := map[string]int{ "pears": 20, "grapes": 35, "kiwis": 15, } // 批量添加 for key, value := range newItems { inventory[key] = value } fmt.Printf(" 批量添加后: %v\n", inventory) // 清空映射 fmt.Printf(" 清空映射:\n") for key := range inventory { delete(inventory, key) } fmt.Printf(" 清空后: %v (len=%d)\n", inventory, len(inventory)) fmt.Println() }// demonstrateMapIteration 演示映射的遍历 func demonstrateMapIteration() { fmt.Println("4. 映射的遍历:") students := map[string]int{ "Alice": 95, "Bob": 87, "Charlie": 92, "David": 78, "Eve": 89, } // 遍历键值对 fmt.Printf(" 遍历键值对:\n") for name, score := range students { fmt.Printf(" %s: %d\n", name, score) } // 只遍历键 fmt.Printf(" 只遍历键:\n") for name := range students { fmt.Printf(" 学生: %s\n", name) } // 只遍历值 fmt.Printf(" 只遍历值:\n") for _, score := range students { fmt.Printf(" 分数: %d\n", score) } // 注意:映射遍历顺序是随机的 fmt.Printf(" 注意: 映射遍历顺序是随机的\n") fmt.Printf(" 多次遍历可能得到不同的顺序\n") // 有序遍历 fmt.Printf(" 有序遍历(按键排序):\n") // 收集所有键 var names []string for name := range students { names = append(names, name) } // 排序键 sort.Strings(names) // 按排序后的键遍历 for _, name := range names { fmt.Printf(" %s: %d\n", name, students[name]) } // 按值排序遍历 fmt.Printf(" 按分数排序遍历:\n") type StudentScore struct { Name string Score int } var studentList []StudentScore for name, score := range students { studentList = append(studentList, StudentScore{name, score}) } // 按分数排序 sort.Slice(studentList, func(i, j int) bool { return studentList[i].Score > studentList[j].Score }) for _, student := range studentList { fmt.Printf(" %s: %d\n", student.Name, student.Score) } fmt.Println() } // demonstrateAdvancedMapOperations 演示映射的高级操作 func demonstrateAdvancedMapOperations() { fmt.Println("5. 映射的高级操作:") // 映射合并 fmt.Printf(" 映射合并:\n") map1 := map[string]int{"a": 1, "b": 2, "c": 3} map2 := map[string]int{"c": 30, "d": 4, "e": 5} fmt.Printf(" map1: %v\n", map1) fmt.Printf(" map2: %v\n", map2) // 合并 map2 到 map1 for key, value := range map2 { map1[key] = value } fmt.Printf(" 合并后: %v\n", map1) // 映射复制 fmt.Printf(" 映射复制:\n") original := map[string]int{"x": 10, "y": 20, "z": 30} copied := make(map[string]int) for key, value := range original { copied[key] = value } fmt.Printf(" 原映射: %v\n", original) fmt.Printf(" 复制映射: %v\n", copied) // 修改复制的映射不影响原映射 copied["x"] = 99 fmt.Printf(" 修改复制后:\n") fmt.Printf(" 原映射: %v\n", original) fmt.Printf(" 复制映射: %v\n", copied) // 映射过滤 fmt.Printf(" 映射过滤:\n") allScores := map[string]int{ "Alice": 95, "Bob": 67, "Charlie": 92, "David": 45, "Eve": 89, "Frank": 78, } // 过滤出及格的学生 passing := make(map[string]int) for name, score := range allScores { if score >= 70 { passing[name] = score } } fmt.Printf(" 所有分数: %v\n", allScores) fmt.Printf(" 及格学生: %v\n", passing) // 映射转换 fmt.Printf(" 映射转换:\n") temperatures := map[string]float64{ "北京": 25.5, "上海": 28.3, "广州": 32.1, } // 摄氏度转华氏度 fahrenheit := make(map[string]float64) for city, celsius := range temperatures { fahrenheit[city] = celsius*9/5 + 32 } fmt.Printf(" 摄氏度: %v\n", temperatures) fmt.Printf(" 华氏度: %v\n", fahrenheit) // 反转映射(键值互换) fmt.Printf(" 反转映射:\n") codeToName := map[int]string{ 1: "Alice", 2: "Bob", 3: "Charlie", } nameToCode := make(map[string]int) for code, name := range codeToName { nameToCode[name] = code } fmt.Printf(" 代码到姓名: %v\n", codeToName) fmt.Printf(" 姓名到代码: %v\n", nameToCode) fmt.Println() } // demonstrateMapAsSet 演示映射作为集合使用 func demonstrateMapAsSet() { fmt.Println("6. 映射作为集合使用:") fmt.Printf(" Go 没有内置的集合类型,可以用 map[T]bool 模拟\n") // 创建集合 fmt.Printf(" 创建集合:\n") fruits := make(map[string]bool) // 添加元素 fruits["apple"] = true fruits["banana"] = true fruits["cherry"] = true fmt.Printf(" 水果集合: %v\n", getKeys(fruits)) // 检查元素是否存在 fmt.Printf(" 检查元素存在:\n") fmt.Printf(" apple 存在: %t\n", fruits["apple"]) fmt.Printf(" grape 存在: %t\n", fruits["grape"]) // 删除元素 delete(fruits, "banana") fmt.Printf(" 删除 banana 后: %v\n", getKeys(fruits)) // 集合运算 fmt.Printf(" 集合运算:\n") set1 := map[string]bool{"a": true, "b": true, "c": true} set2 := map[string]bool{"b": true, "c": true, "d": true} fmt.Printf(" 集合1: %v\n", getKeys(set1)) fmt.Printf(" 集合2: %v\n", getKeys(set2)) // 并集 union := make(map[string]bool) for key := range set1 { union[key] = true } for key := range set2 { union[key] = true } fmt.Printf(" 并集: %v\n", getKeys(union)) // 交集 intersection := make(map[string]bool) for key := range set1 { if set2[key] { intersection[key] = true } } fmt.Printf(" 交集: %v\n", getKeys(intersection)) // 差集 (set1 - set2) difference := make(map[string]bool) for key := range set1 { if !set2[key] { difference[key] = true } } fmt.Printf(" 差集(1-2): %v\n", getKeys(difference)) // 使用 map[T]struct{} 更节省内存 fmt.Printf(" 使用 struct{} 作为值更节省内存:\n") efficientSet := make(map[string]struct{}) efficientSet["item1"] = struct{}{} efficientSet["item2"] = struct{}{} // 检查存在 _, exists := efficientSet["item1"] fmt.Printf(" item1 存在: %t\n", exists) fmt.Println() } // demonstratePracticalApplications 演示映射的实际应用 func demonstratePracticalApplications() { fmt.Println("7. 映射的实际应用:") // 应用1: 计数器 fmt.Printf(" 应用1 - 字符频率统计:\n") text := "hello world" frequency := make(map[rune]int) for _, char := range text { if char != ' ' { // 忽略空格 frequency[char]++ } } fmt.Printf(" 文本: \"%s\"\n", text) fmt.Printf(" 字符频率:\n") for char, count := range frequency { fmt.Printf(" '%c': %d\n", char, count) } // 应用2: 缓存系统 fmt.Printf(" 应用2 - 简单缓存:\n") cache := NewCache() // 设置缓存 cache.Set("user:123", "Alice") cache.Set("user:456", "Bob") // 获取缓存 if value, found := cache.Get("user:123"); found { fmt.Printf(" 缓存命中: user:123 = %s\n", value) } if value, found := cache.Get("user:789"); found { fmt.Printf(" 缓存命中: user:789 = %s\n", value) } else { fmt.Printf(" 缓存未命中: user:789\n") } // 应用3: 配置管理 fmt.Printf(" 应用3 - 配置管理:\n") config := map[string]interface{}{ "database.host": "localhost", "database.port": 5432, "database.ssl": true, "app.debug": false, "app.max_connections": 100, } fmt.Printf(" 配置项:\n") for key, value := range config { fmt.Printf(" %s: %v (%T)\n", key, value, value) } // 应用4: 分组统计 fmt.Printf(" 应用4 - 学生成绩分组:\n") students := []struct { Name string Grade string Score int }{ {"Alice", "A", 95}, {"Bob", "B", 87}, {"Charlie", "A", 92}, {"David", "C", 78}, {"Eve", "B", 89}, {"Frank", "A", 96}, } // 按等级分组 gradeGroups := make(map[string][]string) gradeScores := make(map[string][]int) for _, student := range students { gradeGroups[student.Grade] = append(gradeGroups[student.Grade], student.Name) gradeScores[student.Grade] = append(gradeScores[student.Grade], student.Score) } fmt.Printf(" 按等级分组:\n") for grade, names := range gradeGroups { scores := gradeScores[grade] avg := average(scores) fmt.Printf(" %s级: %v (平均分: %.1f)\n", grade, names, avg) } // 应用5: 索引构建 fmt.Printf(" 应用5 - 倒排索引:\n") documents := map[string]string{ "doc1": "go programming language", "doc2": "python programming tutorial", "doc3": "go web development", "doc4": "java programming guide", } // 构建倒排索引 index := buildInvertedIndex(documents) fmt.Printf(" 文档:\n") for docId, content := range documents { fmt.Printf(" %s: %s\n", docId, content) } fmt.Printf(" 倒排索引:\n") for word, docIds := range index { fmt.Printf(" %s: %v\n", word, docIds) } // 搜索 searchWord := "programming" if docIds, found := index[searchWord]; found { fmt.Printf(" 搜索 '%s': 找到文档 %v\n", searchWord, docIds) } fmt.Println() } // ========== 辅助函数和类型 ========== // 获取映射的所有键 func getKeys(m map[string]bool) []string { var keys []string for key := range m { keys = append(keys, key) } sort.Strings(keys) // 排序以便输出一致 return keys } // 计算平均值 func average(numbers []int) float64 { if len(numbers) == 0 { return 0 } sum := 0 for _, num := range numbers { sum += num } return float64(sum) / float64(len(numbers)) } // 简单缓存实现 type Cache struct { data map[string]string } func NewCache() *Cache { return &Cache{ data: make(map[string]string), } } func (c *Cache) Set(key, value string) { c.data[key] = value } func (c *Cache) Get(key string) (string, bool) { value, exists := c.data[key] return value, exists } func (c *Cache) Delete(key string) { delete(c.data, key) } func (c *Cache) Clear() { c.data = make(map[string]string) } // 构建倒排索引 func buildInvertedIndex(documents map[string]string) map[string][]string { index := make(map[string][]string) for docId, content := range documents { words := strings.Fields(content) for _, word := range words { // 检查是否已经包含该文档 found := false for _, existingDocId := range index[word] { if existingDocId == docId { found = true break } } if !found { index[word] = append(index[word], docId) } } } return index } // 需要导入 strings 包 import "strings" /* 运行这个程序: go run 03-maps.go 学习要点: 1. 映射是键值对的无序集合,键必须是可比较类型 2. 映射是引用类型,零值是 nil 3. 映射的长度是动态的,可以增长和缩减 4. 映射不能直接比较,只能与 nil 比较 5. 映射的遍历顺序是随机的 映射的特性: 1. 键唯一性:每个键只能对应一个值 2. 动态大小:可以动态添加和删除键值对 3. 快速查找:平均 O(1) 时间复杂度 4. 无序性:不保证遍历顺序 5. 引用语义:赋值和传参传递引用 创建映射的方式: 1. 字面量:map[KeyType]ValueType{key: value} 2. make函数:make(map[KeyType]ValueType) 3. 空字面量:map[KeyType]ValueType{} 基本操作: 1. 添加/更新:m[key] = value 2. 读取:value := m[key] 3. 检查存在:value, ok := m[key] 4. 删除:delete(m, key) 5. 长度:len(m) 可比较的键类型: 1. 基本类型:bool, 数值类型, string 2. 指针类型 3. 数组类型(元素可比较) 4. 结构体类型(所有字段可比较) 不可比较的键类型: 1. 切片类型 2. 映射类型 3. 函数类型 4. 包含不可比较字段的结构体 常见应用场景: 1. 缓存和查找表 2. 计数器和统计 3. 配置管理 4. 索引构建 5. 集合操作 6. 分组和聚合 最佳实践: 1. 检查键是否存在避免零值混淆 2. 使用 make 创建空映射而不是 nil 映射 3. 大映射考虑预分配容量 4. 使用 map[T]struct{} 实现集合节省内存 5. 注意映射的并发安全问题 注意事项: 1. nil 映射不能写入,只能读取 2. 映射不是并发安全的 3. 映射的遍历顺序是随机的 4. 删除不存在的键是安全操作 5. 映射的零值是 nil,不是空映射 */