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]
]