map是key-value数据结构,类似于其他语言的集合、字典,在go中被称为"关联数组"
基本语法
var 变量名称 map[键类型]值类型
//golang中的map的类型可以是bool、数组、string、指针、channel、接口、结构体、map
#声明案例
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string //map也可以以map作为key-value
//第二个map作为第一个map的value存在
map须知
一、常见声明和使用方法1、map在使用之前一定要make
2、map的key是不能重复的,如果重复了,则以最后key-value为准
3、map的 value可以相同的
4、map的key-value 是无序的
5、如果不设置make后面的大小,默认是1
//方法1 声明map、再make
var cities map[string]string
cities = make(map[string]string,10)
//方法2 直接make
var cities = make(map[string]string)
//方法3 不make直接赋值
var cities map[string]string = map[string]string{
"no4": "成都"
}
案例1 直接声明map
package main
import(
"fmt"
)
func main(){
//map的声明和注意事项
var a map[string]string //先声明一个map
a = make(map[string]string,10) //我们需要先给map声明一块内存空间才能使用,和切片一样使用make
//10 是我们map的空间大小,如果不写默认是1
a["no1"] = "松江" //赋值时定义 key的名称(no1) 和值"松江"
a["no0"] = "吴用"
a["no3"] = "武松"
a["no4"] = "武松"
a["no5"] = "武松"
a["no6"] = "武松"
fmt.Println(a)
}
返回
map[no0:吴用 no1:松江 no3:武松 no4:武松 no5:武松 no6:武松]
//可以看到,我们在定义时是乱序,但定义好后会自动排序
案例2 直接make
package main
import(
"fmt"
)
func main(){
cities := make(map[string]string) //这样不设置元素大小,是可以自增长的
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
cities["no4"] = "武汉"
cities["no5"] = "深圳"
fmt.Println(cities)
}
返回
map[no1:北京 no2:天津 no3:上海 no4:武汉 no5:深圳]
案例3 直接写入数据
package main
import(
"fmt"
)
func main(){
//直接在定义的时候赋值,就可以不需要make了,并且自增长
heroes := map[string]string{
"hero1": "松江",
"hero2": "黄河",
"hero3": "长江", //这里结尾也是要有逗号的
}
fmt.Println(heroes)
//后续也可以正常加信息
heroes["her4"] = "小河湾"
fmt.Println(heroes)
}
二、map中使用map
比如 我们要存放学生信息,每个学生都是有自己的一些基本信息的,如下
班级(key)
|--stu1 第一个学生 (key)
|--张三 (value)
|--男 (value)
|--201班 (value)
|--stu2 第二个学生 (key)
|--莉莉娅 (value)
|--女 (value)
|--202班 (value)
我们可以把他们看作一个key-value套着另一个key-value 如下,第二套key-value作为第一套K/V的值存在
格式
//格式如下
map[string]map[string]string
案例
package main
import(
"fmt"
)
func main(){
studentsMap := make(map[string]map[string]string) //初始化,分配内存空间,和前面一样
studentsMap["stu01"] = make(map[string]string,3)
//给map下的map分配空间,命名第一个键(key)的名称为stu01
//我们可以把studentsMap["stu01"]看作一个单独的key 等同于map[string]
//每个学生要填写的数据有3种
studentsMap["stu01"]["name"] = "tom" //["name"] = "tom" 等同于map[string]string
studentsMap["stu01"]["sex"] = "男" //作为studentsMap["stu01"]的值存在
studentsMap["stu01"]["address"] = "北京长安街"
studentsMap["stu02"] = make(map[string]string,3) //添加第二个map[string] 同上
studentsMap["stu02"]["name"] = "mary"
studentsMap["stu02"]["sex"] = "女"
studentsMap["stu02"]["address"] = "上海黄浦江"
fmt.Println(studentsMap) //取出整个map下嵌套的所有的值
fmt.Println(studentsMap["stu01"]) //取出第一个key下的所有值
fmt.Println(studentsMap["stu02"]["name"]) //取出第二个key下的name字段的值
}
返回
map[stu01:map[address:北京长安街 name:tom sex:男] stu02:map[address:上海黄浦江 name:mary sex:女]]
map[address:北京长安街 name:tom sex:男]
mary
三、map的增删改查
1、map增加 和更新
package main
import(
"fmt"
)
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no0"] = "吴用"
a["no3"] = "武松"
a["no1"] = "武松" //如果key不存在就会增加,如果存在则覆盖
fmt.Println(a)
}
返回
map[no0:吴用 no1:武松 no3:武松]
2、map删除key
package main
import(
"fmt"
)
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no2"] = "吴用"
a["no3"] = "武松"
fmt.Println(a) //删除前
delete(a,"no1") //通过delete(map,key名称)进行删除
fmt.Println(a) //删除后
}
返回
map[no1:松江 no2:吴用 no3:武松]
map[no2:吴用 no3:武松]
说明
如果我们要删除map的所有key, golang中没有一个专门的方法一次删除,只能遍历
或者map = make(...) make一个新的空间,让原来的变为垃圾,被gc回收
案例 删除所有的key
package main
import(
"fmt"
)
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no2"] = "吴用"
a["no3"] = "武松"
//直接make一个新的空间即可
a = make(map[string]string)
fmt.Println(a)
}
返回
map[]
3、map查询
package main
import "fmt"
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no2"] = "吴用"
a["no3"] = "武松"
value,findRes :=a["no1"] //如果a这个map中存在"no1"的key,那么value会获取值
//而findRes会根据他是否存在值,返回true或false
if findRes{ //判断是否存在该值去输出
fmt.Println("findRes true",value)
}else{
fmt.Println("findRes false")
}
}
返回
findRes true 松江
4、遍历map
map 的遍历使用for-range,不能使用for循环
案例1
package main
import "fmt"
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no2"] = "吴用"
a["no3"] = "武松"
for k,v := range a{
fmt.Printf("k=%v v=%v\n",k,v)
}
}
返回
k=no1 v=松江
k=no2 v=吴用
k=no3 v=武松
案例2
package main
import(
"fmt"
)
func main(){
studentsMap := make(map[string]map[string]string)
studentsMap["stu01"] = make(map[string]string,3)
studentsMap["stu01"]["name"] = "tom"
studentsMap["stu01"]["sex"] = "男"
studentsMap["stu01"]["address"] = "北京长安街"
studentsMap["stu02"] = make(map[string]string,3)
studentsMap["stu02"]["name"] = "mary"
studentsMap["stu02"]["sex"] = "女"
studentsMap["stu02"]["address"] = "上海黄浦江"
for k1, v1 := range studentsMap{ //因为上面的值是map套map的,所以我们要套双层的for-range
fmt.Println("k1=",k1) //第一层for-range将map的value拿出来,交给下一个循环提取
for k2, v2 := range v1{
fmt.Printf("\t k2=%v v2=%v\n",k2,v2)
}
fmt.Println()
}
}
返回
k1= stu01
k2=name v2=tom
k2=sex v2=男
k2=address v2=北京长安街
k1= stu02
k2=name v2=mary
k2=sex v2=女
k2=address v2=上海黄浦江
5、统计map的key-values数量
package main
import "fmt"
func main(){
var a map[string]string
a = make(map[string]string,10)
a["no1"] = "松江"
a["no2"] = "吴用"
a["no3"] = "武松"
//用len统计map有多少对key-value
fmt.Println(len(a))
}
//3
6、map切片
map数据类型的切片我们称为slice of map,通过切片使得map个数可以动态变化了
什么意思呢? 在这里我们要关注的不是map,而是切片。前面我们得知切片是一种动态扩展的类型
我们这里把每个map看作是存放在切片中一个单独的元素,我们实际上是在切片中添加了多个map类型的数据而且
package main
import "fmt"
func main(){
var monsters []map[string]string //在定义map的时候指定他是一个切片类型[]
monsters = make([]map[string]string,2) //分配内存空间 cap=2
fmt.Printf("%T\n",monsters) //[]map[string]string 一个用于存放map类型的切片
if monsters[0] == nil{ //monsters[0]指定切片的第一个元素,当然这里肯定是空
monsters[0] = make(map[string]string,2) //因为切片的类型是map,里面只能存放map数据,所以需要初始化map元素
monsters[0]["name"] = "米缸" //指定第一个元素,去调用map 用法和正常的map一样了
monsters[0]["age"] = "1500"
}
if monsters[1] == nil{
monsters[1] = make(map[string]string,2)
monsters[1]["name"] = "阿周娜"
monsters[1]["age"] = "10000"
}
fmt.Println(monsters) //输出整个切片
fmt.Println(monsters[0]) //输出切片的第一个元素
fmt.Println(monsters[1]) //输出切片的第二个元素
}
返回
[]map[string]string
[map[age:1500 name:米缸] map[age:10000 name:阿周娜]]
map[age:1500 name:米缸]
map[age:10000 name:阿周娜]
错误案例
我们开发中,很难确认我们要写入多少个值,比如我下面多加了一个元素,切片空间就超出了预定2的上限
package main
import "fmt"
func main(){
var monsters []map[string]string
monsters = make([]map[string]string,2)
if monsters[0] == nil{
monsters[0] = make(map[string]string,2)
monsters[0]["name"] = "米缸"
monsters[0]["age"] = "1500"
}
if monsters[1] == nil{
monsters[1] = make(map[string]string,2)
monsters[1]["name"] = "阿周娜"
monsters[1]["age"] = "10000"
}
//我们上面定义了map空间大小为2
//make([]map[string]string,2)
//这里故意多写一个元素,如下
if monsters[2] == nil{
monsters[2] = make(map[string]string,2)
monsters[2]["name"] = "哈哈怪"
monsters[2]["age"] = "30"
}
fmt.Println(monsters)
fmt.Println(monsters[0])
fmt.Println(monsters[1])
fmt.Println(monsters[2])
}
返回
panic: runtime error: index out of range [2] with length 2
goroutine 1 [running]:
main.main()
D:/go_setup/go1.17/src/test/main/main.go:28 +0x4f
7、map空间动态增长
还记得前面我们整切片的时候,有个append函数吗 对就是这个
package main
import "fmt"
func main(){
var monsters []map[string]string
monsters = make([]map[string]string,2)
if monsters[0] == nil{
monsters[0] = make(map[string]string,2)
monsters[0]["name"] = "米缸"
monsters[0]["age"] = "1500"
}
if monsters[1] == nil{
monsters[1] = make(map[string]string,2)
monsters[1]["name"] = "阿周娜"
monsters[1]["age"] = "10000"
}
//我们上面定义了map空间大小为2
//make([]map[string]string,2)
//这里故意多写一个元素,如下
//if monsters[2] == nil{
// monsters[2] = make(map[string]string,2)
// monsters[2]["name"] = "哈哈怪"
// monsters[2]["age"] = "30"
//}
//我们单独去定义一个map,然后通过append将map加入到切片中
newMonster := map[string]string{
"name": "哈哈怪",
"age": "30",
}
monsters = append(monsters,newMonster) //将map添加到切片中
fmt.Println(monsters)
fmt.Println(monsters[0])
fmt.Println(monsters[1])
fmt.Println(monsters[2])
}
返回
[map[age:1500 name:米缸] map[age:10000 name:阿周娜] map[age:30 name:哈哈怪]]
map[age:1500 name:米缸]
map[age:10000 name:阿周娜]
map[age:30 name:哈哈怪]
8、map的排序
go 中map 默认是无序的,并且没有针对map key的排序
(我很想说这句话ヽ(≧□≦)ノ,但我自己试验时全都是排序好的是___*(  ̄皿 ̄)/#____,不知道是不是新版优化了的原因)就当学个新模块吧
package main
import (
"fmt"
"sort"
)
func main(){
map1 := make(map[int]int,10)
map1[10] = 100
map1[1] = 13
map1[4] = 56
map1[8] = 90
var keys []int //定义一个切片
for k, _ := range map1{ //将map的key 循环出来存放到切片中
keys = append(keys,k)
}
sort.Ints(keys) //通过sort模块的Ints对整数进行排序
fmt.Println(keys)
for _, k :=range keys{ //按照切片中key的顺序去取map的值
fmt.Printf("map[%v]=%v\n",k ,map1[k])
}
}
四、小练习
1. 使用map[string]map[string]string
2. key 表示用户,是唯一的,不可重复的
3. 如果某个用户存在,就将其密码修改"888888",如果不存在就增加这个用户的信息
//包括 昵称nickname和密码pwd
4. 编写一个函数 modifyUser(users map[string]map[string]string),name string
代码
package main
import "fmt"
func modifyUser(users map[string]map[string]string, name string) { //传入的参数为 map嵌套map的类型 和一个string
if users[name] != nil{ //判断是否有用户,这里name是有值的,所以不为空
users[name]["pwd"] = "888888" //有用户则添加密码
}else {
users[name] = make(map[string]string,2) //没有则会去声明map,key的名称是name,就定义成功了
users[name]["pwd"] = "888888"
users[name]["nickname"] = "昵称" + name
}
}
func main(){
users := make(map[string]map[string]string, 10)
modifyUser(users,"tom")
modifyUser(users,"mary")
modifyUser(users,"xxx")
//传一个自己有值的map
users["smith"] = make(map[string]string,2)
users["smith"]["pwd"] = "999999"
users["smith"]["nickname"] = "小花猫"
//调用这个已有的名称会怎么样
modifyUser(users,"smith")
fmt.Println(users)
}
返回
map[
mary:
map[
nickname: 昵称mary
pwd:888888
]
smith:
map[
nickname:小花猫
pwd:888888
]
tom:
map[
nickname:昵称tom
pwd:888888
]
xxx:
map[
nickname:昵称xxx
pwd:888888]
]