Go语言中的map
Go语言中的map
Go语言中的map是一种内置的数据结构,用于存储键值对(key-value pairs)。map是无序的,并且是可变的。它是实现关联数组、哈希表(hash table)或者字典(dictionary)功能的理想工具。
Go语言中的map由make()函数创建,或者直接通过字面量定义。
map的键和值都可以是任何类型,但键必须是可比较的(支持==和!=运算符的类型)。
使用make()创建map
m := make(map[string]int) // 创建一个键为string类型,值为int类型的map
m := map[string]int{
"a": 1,
"b": 2,
"c": 3,
} // 创建并初始化一个map
map的基本操作
m := make(map[string]int)
m["a"] = 1 // 插入一个键值对,"a"键对应的值为1
m["b"] = 2 // 插入另一个键值对,"b"键对应的值为2
m["a"] = 3 // 更新"b"键的值,新的值为3
fmt.Println(m) // 输出: map[a:3 b:2]value := m["a"] // 获取键为"a"的值
fmt.Println(value) // 输出: 3
- 检查键是否存在
当使用map获取元素时,如果键不存在,返回值的默认值(对于int类型是0)和false。你可以通过第二个返回值检查键是否存在。
value, exists := m["a"]
if exists {fmt.Println("Value:", value)
} else {fmt.Println("Key does not exist.")
}value, exists = m["z"]
fmt.Println(exists) // 输出: false,表示键"z"不存在
- 删除元素
delete(m, "a") // 删除键为"a"的元素
fmt.Println(m) // 输出: map[b:2]
- map的遍历
Go提供了range语句来遍历map,通过range遍历时,你可以获得每个键值对。
m := map[string]int{"a": 1, "b": 2, "c": 3}for key, value := range m {fmt.Println(key, value)
}a 1
b 2
c 3注意,map是无序的,因此遍历的顺序每次可能不同。
map的特性
键的类型
Go语言中的map要求键是可比较的类型(即支持==和!=运算符)。
常见的可比较类型包括数字、字符串、指针、接口、数组等。但切片、函数、map本身是不可比较的,因此不能作为map的键。
// 正确
m := make(map[string]int)// 错误,不能用切片作为键
var m2 map[[]int]int
- map的容量和增长
map在内部会根据需要自动扩展容量。
如果map的容量不足,它会自动增加其容量,通常以倍数扩展。
但要注意,map的容量是动态的,并且在插入时没有直接提供容量控制。
可以使用len()函数获取map的元素个数。 fmt.Println(len(m)) // 输出: 2
- map是引用类型
map是引用类型,这意味着当你将map赋值给一个新的变量时,两个变量都指向同一块内存区域,修改其中一个会影响另一个。
m1 := map[string]int{"a": 1, "b": 2}
m2 := m1 // m2与m1指向相同的map
m2["a"] = 100
fmt.Println(m1) // 输出: map[a:100 b:2]
fmt.Println(m2) // 输出: map[a:100 b:2]
- map的性能
map的性能通常是非常高效的,特别是在查找、插入和删除操作上,平均时间复杂度为O(1)(常数时间)。
但是,在极端情况下,map的性能可能会受到哈希冲突的影响,导致性能下降。Go的map会动态调整大小和哈希桶,以尽量减少冲突。
- 空map
一个空map可以通过make()函数创建。
注意,未初始化的map(即直接声明而没有通过make()或字面量初始化的map)为nil,对nil的map进行插入会引发运行时错误。
var m map[string]int // 未初始化的map,默认为nil
fmt.Println(m) // 输出: map[]m = make(map[string]int) // 正确,创建空map
m["a"] = 1
fmt.Println(m) // 输出: map[a:1]
常见使用场景
map在Go中有许多实际应用场景,包括:
去重:可以使用map来去重,例如从切片中去除重复的元素。
频率统计:通过map统计某个元素出现的次数,常用于文本处理。
缓存/字典:作为简单的缓存或者字典,存储频繁查询的数据。
统计字符频率str := "hello world"
freq := make(map[rune]int)// 遍历字符串,统计字符频率
for _, char := range str {
freq[char]++
}// 打印字符频率表
for key, value := range freq {
fmt.Printf("'%c': %d\n", key, value)
}
去重
func removeDuplicates(nums []int) []int {unique := make(map[int]bool) // map的值可以是任意类型,bool类型只用于标记是否存在var result []intfor _, num := range nums {if _, exists := unique[num]; !exists { // 如果map中没有该元素unique[num] = trueresult = append(result, num) // 添加到结果切片}}return result
}
func main() {nums := []int{1, 2, 3, 2, 4, 1, 5}uniqueNums := removeDuplicates(nums)fmt.Println(uniqueNums) // 输出: [1 2 3 4 5]
}
使用 map[int]bool 来存储已经出现过的数字,值设为 true,表示该数字已经出现过。遍历原始切片,若 map 中不存在该元素,则将其加入到结果切片中。由于 map 中的键是唯一的,重复的数字会被自动忽略。