This commit is contained in:
2025-08-24 01:15:18 +08:00
parent e51feb1296
commit ec23aa5c26
6 changed files with 3631 additions and 3 deletions

View File

@@ -0,0 +1,722 @@
/*
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不是空映射
*/