继续
This commit is contained in:
722
golang-learning/04-data-structures/03-maps.go
Normal file
722
golang-learning/04-data-structures/03-maps.go
Normal 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,不是空映射
|
||||
*/
|
Reference in New Issue
Block a user