1.1 什么是map
在 Go 语言中,map 是一种无序的基于 key-value 的数据结构,又称为字段或关联数组。类似其它语言的集合,在编程中比较常用
定义和使用 map,有以下步骤:
① 声明 map
② 初始化 map
③ 向 map 中添加元素(赋值)
④ 从 map 中读取数据
1.2 声明map,基本语法:
var 变量名 map[keytype]valuetype
比如:
//声明 map,key 类型是 string,value 类型是 string
var m1 map[string]string
//声明 map,key 类型是 int,value类型是 string
var m2 map[int]string
var m3 map[string]int
//声明一个二维 map
var m4 map[string]map[string]string
2.1 初始化map:
Go 语言中的 map 是引用类型,如上面那样,只声明 map 并不会分配内存,必须使用 make 初始化后,才能赋值和使用(官方对 make 的描述为:内建函数 make 分配并初始化一个类型为切片、映射、或通道的对象)。map 初始化举例:
//声明 map
var m1 map[string]string
//使用 make 初始化 map
m1 = make(map[string]string)
//使用类型推导直接声明并初始化
m2 := make(map[int]string)
注:make 时可以设置容量(选填参数),map 的容量达到后,继续向 map 增加 key-value,map 会自动扩容,并不会发生 panic,也就是说map 能动态增的增加键值对。
2.2 map赋值
map 的“声明、初始化、赋值”有多种方式,可以先声明并初始化,然后再赋值,也可以使用类型推导直接声明初始化并赋值
//方式一:声明 map
var m1map[string]string
//使用 make 给 map 分配数据空间
m1 = make(map[string]string)
//赋值,注意key和value必须使用双引号,int类型无需引号
m1["no1"] = "Alice"
m1["no2"] = "Bob"
fmt.Println(m1)
//方式二:使用类型推导直接声明并初始化map
m2 := make(map[int]string)
m2[1] = "Audi"
m2[3] = "Benz"
m2[5] = "BMW"
fmt.Println("m2 =", m2)
//map使用 for-range 遍历
for k, v := range m2 {
fmt.Println("k=", k, "v=", v)
}
//方式三:使用类型推导声明初始化map并赋值
m3 := map[string]string{
"no1": "Tom",
"no2": "Jack",//此种格式最后必须加逗号
}
m3["no3"] = "Bob" //可以继续赋值
fmt.Println(m3)
2.3 多维map
map 内可以嵌套 map,注意 map 内部的 map,每次都需要 make 初始化后,才能赋值
//声明并初始化二维map
m3 := make(map[string]map[string]string)
m3["no1"] = make(map[string]string) //每次赋值需要再次 make
m3["no1"]["name"] = "Alice"
m3["no1"]["age"] = "30"
m3["no2"] = make(map[string]string) //每次赋值需要再次 make
m3["no2"]["name"] = "Bob"
fmt.Println("m3 =", m3)
//二维map,直接赋值
m4 := map[string]map[string]string{
"no1": map[string]string{"name": "Alice", "age": "30"}, //此种格式,map内部的最后一个元素结尾不用加逗号
"no2": map[string]string{
"name": "Bob",
"age": "20", //此种格式最后必须加逗号
}, //此处逗号不能省略
}
m4["no3"] = make(map[string]string)
m4["no3"]["name"] = "Tom"
m4["no4"] = map[string]string{
"name": "Jack",
}
fmt.Println("m4 =", m4)
//三维map案例
m5 := map[string]map[string]map[string]string{
"no1": map[string]map[string]string{ //map中的map需要再次初始化
"user1": map[string]string{"name": "Alice", "age": "30"}, //map中的map需要再次初始化
"user2": map[string]string{"name": "Bob", "age": "20"},
},
"no2": map[string]map[string]string{
"school": map[string]string{"name": "PKU"},
},
}
fmt.Println("m5 =", m5)
3.1 map添加 struct 结构体
map 的 value 也经常使用 struct 类型,更适合管理复杂的数据
//定义结构体
type Val struct {
field string
name string
require bool
}
func main(){
//map 添加结构体
m7 := map[int]Val{
0: Val{"aa", "bb", true},
1: Val{"cc", "dd", false},
2: Val{"ee", "ff", true},
}
fmt.Println("m7 =", 7)
}
3.2 map切片 slice
切片的数据类型如果是 map,可以称之为 map 切片,map切片中的 map 个数可以动态变化
//方式一:声明map切片
var m6 []map[string]string
//初始化map切片,make时必须声明切片长度(切片超出此长度会报错)
m6 = make([]map[string]string, 1)
//切片内的map需要再次make初始化
m6[0] = make(map[string]string)
//赋值
m6[0]["name"] = "Alice"
m6[0]["age"] = "30"
//m6[1] = map[string]string{"name": "Jack"} //上面make时切片长度设置为1,此处超出切片长度,编译时报错提示:index out of range [...] with length ...
方式二:make初始化并赋值
var m6 []map[string]string
m6 = make([]map[string]string, 0) //默认给0,超出长度自动延伸,有性能损耗
m6[0] = map[string]string{"name": "Bob", "age": "20"}
m6[1] = map[string]string{
"name": "John",
"age": "30",
}
方式三:更简单的使用方式
var m6[]map[string]string
m6 = []map[string]string {
{"name": "**", "type": "**"},
{"name": "**", "type": "**"},
{"name": "**", "type": "**"},
{"name": "**", "type": "**"},
}
fmt.Println("m6 =", m6)
3.3 map接口 interface{}
接口(interface)是 Go 语言中的一种类型,它可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
//方式一:声明 map
var m map[string]interface{}
//初始化
m = make(map[string]interface{})
m["name"] = "Alice"
m["age"] = "30"
m["gerden"] = "man"
fmt.Println("m =", m)
//方式二:类型推导
m := make(map[string]interface{})
m["name"] = "Alice"
fmt.Println("m =", m)
//方式三:多维map。声明 interface{} 类型后,可以是多维 map
m := make(map[string]interface{})
m["no1"] = map[string]string{
"name": "Alice",
"age": "30",
}
fmt.Println("m=", m)
使用 interface{} 作为 map 的键或值类型时,需要注意以下几点:
类型安全:由于 interface{} 可以存储任何类型,这使得类型检查变得不那么严格。在运行时,需要确保正确地处理不同类型的值。例如,当从 map 中取出一个值并尝试将其转换为特定类型时,需要使用类型断言或类型转换,不断言会提示错误:...variable of type interface{}
性能考虑:使用 interface{} 会导致运行时类型检查,这可能会比直接使用具体类型慢一些。因此,在性能敏感的应用中,应谨慎使用。
//myMap是map[string]interface{}类型
myMap := make(map[string]interface{})
myMap["one"] = 1
myMap["two"] = 2
value, ok := myMap["two"].(int) //断言,由于Go不确定interface{}中存了什么类型,所以需要做判断,尝试将 "two" 的值转换为int类型
if ok {
fmt.Println("value: ", value) //转换成功,打印值
} else {
fmt.Println("failed") //转换失败,打印错误信息
}
更详细的案例,从接口中拿到 json 数据,转换为 map
//创建 map[],给 map[] 申请一块内存空间
var result map[string]interface{} //使用 interface{} 来存储任意类型的字段值
//body 是接口返回的 json 串,多维 map,通过把 json.Unmarshal 方法把 json 数据转为 map 无序 key-value 键值对
err := json.Unmarshal([]byte(body), &result)
if err != nil {
panic(err)
}
//处理结果
if result["result"] == "OK" {
//result["data"]是多维的,类型是切片:[]interface{}
for _, v := range result["data"].([]interface{}) {
//外层是切片 []interface{},由于 interface{} 的值非常广泛,所以这里需要断言(判断v的值是什么类型,这里的v是map[string]interface{})
tmp, ok := v.(map[string]interface{}) //等价于这种写法 if tmp,ok := v.(map[string]interface{});ok {} ,判断 v 的类型
if ok {
//map[string]interface{} 内部的字段,全部需要断言(接口返回的参数,Go需要强制数据类型,所以都需要断言)
roomId, rok := tmp["roomId"].(string)
liveStatus, lok := tmp["liveStatus"].(float64)
if rok && lok {
data[roomId]["live_status"] = strconv.FormatInt(int64(liveStatus), 10)
}
}
}
} else {
panic(result["reason"].(string))
}
4.1 获取map长度
获取 map 长度,使用 go 内置的 len 函数,无需引入其它包
m := map[string]string{
"3": "Alice",
"5": "Bob",
"8": "Tom",
}
fmt.Println("m lenth = ", len(m))
4.2 map的增删改查操作
① map 的增加和更新,如果 key 没有就是增加,如果 key 存在就是修改
map["key"] = value //如果 key 没有就是增加,如果 key 存在就是修改
比如:
//声明map
var m1 map[string]string
//使用make给map分配数据空间
m1 = make(map[string]string)
//赋值
m1["no1"] = "Australia"
m1["no2"] = "Canada"
m1["no1"] = "Japan" //no1已存在,此处将覆盖原来的值
fmt.Println("m1[\"no1\"] =", m1["no1"])
② map删除,使用 delete 函数,存在即删除,map 为 nil 或 key 不存在,不进行任何操作
delete(map, "key") //delete是一个内置函数,如果key存在,就会删除对应的 key-value,如果key不存在,不进行操作,也不会报错
比如:
m := map[string]string{
"no1": "Tom",
"no2": "Jack",
}
delete(m, "no1") //删除 m 中的 no1
批量删除:
Go 并没有提供批量删除 map 的函数,只能对 map 遍历后逐个删除。若要将 map 中的元素全部删除,可以 make 一个新的 map,将原来的 map 进行替换,原来的 map 将成为垃圾被 gc 回收
③ map查找
m := map[string]string{
"name": "Bob",
"Age": 30",
}
val, flag := m[name] //查找 name 是否存在于m的key中
4.3 map排序
Go语言中的 map 是无序的,每次遍历,得到的输出可能都不同,且 Golang 中没有一个专门的方法针对 map 的 key 进行排序,如果想对 map 进行排序,需要进行以下三步:
① 遍历 map,将 map 的 key 值存入一个切片
② 对切片进行排序
③ 遍历切片,输出对应的 map 的值
//引入sort包,其内部有排序函数
import "sort"
//使用类型推导创建map并赋值
m := map[int]string{
0: "Alice",
1: "Tom",
2: "Brown",
}
//创建一个切片
var keys []int
//遍历map,获取map的key
for k, _ := range m {
//将map的key添加到切片中
keys = append(keys, k)
}
//使用 sort 包的 Ints 函数对切片排序
sort.Ints(keys)
fmt.Println(keys)
4.4 map的传递类型
在 Go 语言中,map 是引用类型,这意味着将一个 map 赋值给另一个变量,或者作为参数传递给函数时,实际上传递的是 map 的引用,而不是 map 的副本。所以在函数内部对 map 所做的任何修改都会影响到原始的 map。
通俗来讲,就是将map赋值给另一个变量后,增删改原map或新map内的值,两个map都会发生变化,比如下面的案例:

对于把 map 赋值给另一变量的需求,建议创建一个新 map,遍历旧 map 对新 map 进行逐个赋值,比如:

① map 在使用前一定要 make
② map 的 key 不能重复,如果重复了,将会覆盖前面的值,以最后这个 key-value 为准
③ map 的 value 是可以相同的
④ map 的 key-value 是无序的,其每次的输出顺序是随机的
⑤ map 中的 key-value类型一旦声明,不能被改变,比如声明为 string 类型,却给了 int 类型,编译时会报错
⑥ map 中嵌套 map 时,内部的 map 赋值时需要再次使用 make 初始化
上一篇:Go语言slice切片
下一篇:GO设置代理