map 是key-value 的数据结构,又称为字段或者关联数组,类似其他编程语言的集合
一、map的声明、初始化
1、map的声明、初始化
-
声明:var 变量名 map[键类型]值类型
-
初始化:变量名 = make(map[键类型]值类型, [初始空间大小]),空间大小可选
-
例如:
var map1 map[string]int
map1 = make(map[string]int, 10) // 10 是可选参数,表示初始分配的空间大小,单位是元素个数
2、细节
-
map 声明的时候是不会分配内存的,不能直接使用,需要使用 make进行初始化分配空间后才能用
-
键类型可以是很多类型,比如 bool 数字 string 指针 channel 接口 结构体 数组等。但通常更多的是 string 和 int
-
键类型不能是 map slice 和function,因为这几个没办法用 == 来判断
-
值类型可以是任意类型,包括 map slice function 等,但通常为 数字 string map 结构体
-
键类型唯一,如果重复了,则以最后一个 key-value 为准
-
值类型是可以相同的
-
map 的 key-value是无序的
3、示例
package main
import "fmt"
func main() {
// 声明 map 但不初始化
var map1 map[int]int
var map2 map[string]string
var map3 map[string]map[string]int
var map4 map[string][2]int
var map5 map[string]struct{
name string
age int
}
fmt.Println(map1, map2, map3, map4, map5) // 输出:map[] map[] map[] map[] map[]
// 声明+初始化
var m1 map[int]int
// map[0] = 1 // 报错,没有初始化之前是不能使用的
m1 = make(map[int]int, 10) // 使用make 进行初始化
map[0] = 1 // 初始化后就可以使用了
fmt.Println(m1) // 输出:map[0:1]
}
二、map的使用
- 方式1:先声明然后使用 make进行初始化
var m1 map[int]int
m1 = make(map[int]int, 10)
m1[1] = 10
- 方式2:使用 map字面量进行声明和初始化
m2 := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
- 方式3:使用 make 创建并初始化
m1 := make(map[int]int)
m1[0] = 10
m1[1] = 20
三、map的增删改查操作
-
增加和更新 map:mapName[key] = value,如果 key 已经存在,则更新对应的 value;如果 key 不存在,则添加新的 key-value 对
-
删除 map 中的元素:使用 delete(mapName, key) 函数删除 map 中的元素,如果 key 存在,则删除对应的 key-value 对;如果 key 不存在,则 delete 不执行任何操作
-
查询 map 中的元素:使用 value, isFind = mapName[key] 来查询 map 中的元素,如果 key 存在,则返回对应的 value;
-
如果要一次性删除,可以使用 for循环删,或者直接给 map 重新 make 一个新的空 map 来覆盖原来的 map
package main
import "fmt"
func main() {
m1 := make(map[int]int)
// 增加
m1[0] = 10 // 增加 key=0 value=10
m1[1] = 20 // 增加 key=1 value=20
m1[2] = 30 // 增加 key=2 value=30
fmt.Println("增加后 map:", m1) // 输出:增加后 map: map[0:10 1:20 2:30]
// 更新 key=0 的 value
m1[0] = 100 // 更新 key=0 的 value=100
// 查询 key=0
value, isFind := m1[0] // 查询 key=0
if isFind {
fmt.Println("key=0 存在,value=", value) // 输出:key=0 存在,value=100
} else {
fmt.Println("key=0 不存在")
}
// 删除 key=1
delete(m1, 1) // 删除 key=1
// 全部删除
// 方法1:使用 for循环删除
for k := range m1 {
delete(m1, k)
}
// 方法2:直接 make 一个新的空 map 来覆盖原来的 map
m1 = make(map[int]int)
fmt.Println("删除后 map:", m1) // 输出:删除后 map: map[]
}
四、map的遍历、长度
-
for...range 遍历
-
len(map) 获取 map 中 key-value 对的数量
package main
import "fmt"
func main() {
m1 := make(map[int]int)
m1[0] = 10
m1[1] = 20
// 遍历 map
for k, v := range m1 {
fmt.Printf("key: %d, value: %d\n", k, v)
}
// 获取 map 的长度
fmt.Println("map 的长度:", len(m1)) // 输出:map 的长度
}
五、map 切片
- map 切片是指切片中的元素是 map 类型的切片
var mapSlice []map[string]int
- map 切片的声明和初始化
package main
import "fmt"
func main{
var mapSlice []map[string]int
mapSlice = make([]map[string]int, 3) // 创建一个长度为3 的 map 切片
// 遍历初始化 map 切片中的每个 map 元素
for i := range mapSlice {
mapSlice[i] = make(map[string]int) // 初始化每个 map 元素
}
// map 切片的使用
mapSlice[0]["a"] = 1 // 给第一个 map 元素添加 key="a" value=1
mapSlice[1]["b"] = 2 // 给第二个 map 元素添加 key="b" value=2
mapSlice[2]["c"] = 3 // 给第三个 map 元素添加 key="c" value=3
fmt.Println("map 切片:", mapSlice) // 输出:map
}
六、map的排序
map 是无序的,如果需要对 map 进行排序,可以将 map 的 key 提取到一个切片中,对切片进行排序,然后按照排序后的 key 来访问 map 中的元素
package main
import (
"fmt"
"sort"
)
func main() {
// 1、声明初始 string类型的 map
m1:= make(map[string]int)
m1["c"] = 3
m1["a"] = 1
m1["b"] = 2
m1["d"] = 4
keys := make([]string, 0, len(m1)) // 创建一个切片来存储 map 的 key
for k := range m1 {
keys = append(keys, k) // 将 map 的 key 添加到切片中
}
keys = sort.Strings(keys) // 对切片中的 key 进行排序
for _, k := range keys {
fmt.Printf("key: %s, value: %d\n", k, m1[k]) // 按照切片中的 key 来访问 map 中的元素
}
// 声明一个 int 类型的 map
m2 := make(map[int]int)
m2[0] = 10
m2[3] = 40
m2[1] = 20
var keys = []int
for k := range m2 {
keys = append(keys, k) // 将 map 的 key 添加到切片中
}
sort.Ints(keys) // 对切片中的 key 进行排序
for _, k := range keys {
fmt.Printf("key: %d, value: %d\n", k, m2[k]) // 按照切片中的 key 来访问 map 中的元素
}
}
七、map的使用细节
-
map是引用类型,map变量存储的是map的地址,多个map变量可以指向同一个map,如果通过其中一个变量修改了map的内容,其他变量也会看到修改后的结果
-
map的零值是 nil,nil map 不能添加 key-value 对,但可以查询和删除 key-value 对,查询 nil map 中的任何 key 都会返回对应类型的零值,删除 nil map 中的任何 key 都不会有任何效果
-
map 的并发访问:map 不是线程安全的,如果多个 goroutine 同时读写同一个 map,可能会导致程序崩溃或者数据不一致,因此在并发场景下需要使用 sync.Mutex 或者 sync.RWMutex 来保护 map 的访问
-
map 的容量到达后,再添加匀速,并不会报错,但会自动扩容,扩容后的 map 会重新哈希所有的 key,因此在性能敏感的场景下,建议在创建 map 时就指定一个合理的初始容量,以减少扩容的次数和开销
-
map 的value经常是 struct类型
package mian
import "fmt"
func changeMap(map[int]int) {
m[0] = 100
}
func main() {
// map是引用类型
m1 := map[int]int
m1 = make(map[int]int)
m1[0] = 10
changeMap(m1)
fmt.Println(m1) // 输出:map[0:100]
// 2、nil map 不能添加 key-value 对,但可以查询和删除 key-value 对
var m2 map[string]string // 声明一个 nil map
fmt.Println(m2) // 输出:map[]
// m2["a"] = "1" // 报错,不能向 nil map 中添加 key-value 对
value := m2["a"] // 查询 nil map 中的 key "a",返回零值 ""
fmt.Println(value) // 输出:"",map[string]string 的零值是 ""
delete(m2, "a") // 删除 nil map 中的 key "a",不会有任何效果
// 3、声明一个 map,键为 string 类型,值为 struct 类型
type User struct {
name string
age int
}
m1 := make(map[string]User)
m1["user1"] = User{name: "Alice", age: 30}
m1["user2"] = User{name: "Bob", age: 25}
fmt.Println("map with struct value:", m1) // 输出:map with struct value: map[user1:{Alice 30} user2:{Bob 25}]
}