722 lines
17 KiB
Go
722 lines
17 KiB
Go
/*
|
||
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,不是空映射
|
||
*/ |