1. map的基本介绍
map是key-value数据结构,又称为字段或者关联数组。类似其它编程语言的集合,在编程中是经常使用到的
2. map的声明
1)基本语法
var map 变量名 map[keytype]valuetype
* key可以是什么类型
golang中的map的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是只包含前面几个类型的接口,结构体,数组
通常key为int、string
注意:slice,map还有function不可以,因为这几个没法用==来判断
* valuetype 可以是什么类型
valuetype的类型和key基本一样,这里就不再叙述了
通常为:数字、string、map、struct
2)map声明的举例:
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string
注:声明是不回分配内存的,初始化需要make,分配内存后才能赋值和使用
案例演示:
package main
import (
"fmt"
)
func main() {
// 演示map的声明和注意事项
var a map[string]string
// 在使用map前,需要先make,make的作用就是给map分配数据空间
a = make(map[string]string,10)
a["no1"] = "宋江"
a["no2"] = "武松"
a["no1"] = "吴用"
a["no3"] = "吴用"
fmt.Println(a)
}
* 对上面代码的说明:
1)map在使用前一定要make
2)map的key是不能重复的,如果重复了(执行修改),则以最后这个key-value为准
3)map的value是可以相同的
4)map的key-value是无序的
5)make的内置函数数目
3. map的使用方式
package main
import (
"fmt"
)
func main() {
// 如果给map赋值超出容量,会自动扩容
// 1. 第一种使用方式
var map1 map[string]string
map1 = make(map[string]string,1)
map1["no1"] = "宋江"
map1["no2"] = "吴用"
fmt.Println("map1", map1)
// 2. 第二种使用方式
map2 := make(map[string]string)
map2["no1"] = "北京"
map2["no2"] = "天津"
map2["no3"] = "上海"
fmt.Println("map2",map2)
// 3. 第三种使用方式
var map3 map[string]string = map[string]string{
"no1" : "宋江",
"no2" : "卢俊义",
"no3" : "吴用",
}
fmt.Println("map3",map3)
}
* map使用的课堂案例
演示一个key-value 的value是map的案例
比如:我们要存放3个学生信息,每个学生有name和sex信息
package main
import (
"fmt"
)
func main() {
// 课堂案例:
// 演示一个key-value 的value是map的案例
// 比如:我们要存放3个学生信息,每个学生有name和sex信息
// 思路: map[string]map[string]string
studentMap := make(map[string]map[string]string)
studentMap["stu01"] = make(map[string]string)
studentMap["stu01"]["name"] = "mary"
studentMap["stu01"]["sex"] = "女"
studentMap["stu01"]["address"] = "北京"
fmt.Println("studentMap",studentMap)
studentMap["stu02"] = make(map[string]string)
studentMap["stu02"]["name"] = "tom"
studentMap["stu02"]["sex"] = "男"
studentMap["stu02"]["address"] = "江西"
fmt.Println("studenMap",studentMap)
studentMap["stu03"] = make(map[string]string)
studentMap["stu03"]["name"] = "carry"
studentMap["stu03"]["sex"] = "男"
studentMap["stu03"]["address"] = "广东"
fmt.Println("student",studentMap)
}
4. map的增删改查操作
1)map的增加和更新
map["key"] = value // 如果key还没有,就是增加,如果key存在就是修改操作
2)map删除
细节说明:
如果我们要删除map的所有key,没有一个专门的方法一次性删除,可以遍历一下key,逐个删除或者 map = make(...),make一个新的,让原来的成为垃圾,被gc回收
3)map查找
val, ok : = map["key"]
如果这个map中存在key,那么findRes就会返回true,否则返回false
以上操作的代码如下:
package main
import (
"fmt"
)
func main() {
// 1. map的增加和更新
// map["key"] = value // 如果key还没有,就是增加,如果key存在就是修改
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)
// 因为no3这个key已经存在,因此下面的这句话就是修改操作
cities["no3"] = "上海~~"
fmt.Println(cities)
// 2. map的删除
// delete(map,"key") delete是一个内置函数,如果key存在就删除该key-value,如果key不存在,不操作,也不报错
delete(cities,"no1")
// 3. map查找
// 如果这个map中存在no2,那么findRes就会返回true,否则返回 false
val, ok := cities["no2"]
if ok {
fmt.Printf("有no2 key 值为%v \n",val)
} else {
fmt.Printf("没有no1 key \n")
}
}
5. map遍历
案例演示相对复杂的map遍历:该map的value又是一个map
说明:map的遍历使用for-range的结构遍历
package main
import (
"fmt"
)
func main() {
// 使用for-range遍历map
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
for k,v := range cities {
fmt.Printf("k=%v, v=%v \n",k,v)
}
// 使用for-range遍历一个结构比较复杂的map
studentMap := make(map[string]map[string]string)
studentMap["stu01"] = make(map[string]string,3)
studentMap["stu01"]["name"] = "tom"
studentMap["stu01"]["sex"] = "男"
studentMap["stu01"]["address"] = "北京长安街"
studentMap["stu02"] = make(map[string]string)
studentMap["stu02"]["name"] = "mary"
studentMap["stu02"]["sex"] = "女"
studentMap["stu02"]["address"] = "上海黄浦江"
for k1,v1 := range studentMap {
fmt.Println("k=",k1)
for k2,v2 := range v1 {
fmt.Printf(" k=%v,v=%v \n",k2,v2)
}
}
}
* map的长度
6. map切片
1)基本介绍
切片的数据类型如果是map,则我们称为slice of map,map切片,这样使用则map个数就可以动态变化了。
案例演示:
要求:
使用一个map来记录monster的信息name和age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加=> map切片
package main
import (
"fmt"
)
func main() {
// 演示map切片的使用
// 要求:使用一个map来记录monster的信息 name 和 age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加 => map切片
// 1. 声明一个map切片
var monsters []map[string]string
monsters = make([]map[string]string,2) // 初始化切片
// 2. 增加第一个妖怪的信息
if monsters[0] == nil {
monsters[0] = make(map[string]string)
monsters[0]["name"] = "牛魔王"
monsters[0]["age"] = "500"
}
// 增加第二个妖怪的信息
if monsters[1] == nil {
monsters[1] = make(map[string]string)
monsters[1]["name"] = "玉兔精"
monsters[1]["age"] = "400"
}
// 下面这个写法越界
// if monsters[2] == nil {
// monsters[2] = make(map[string]string)
// monsters[2]["name"] = "猪八戒"
// monsters[2]["age"] = "1000"
// }
// 这里我们需要使用到切片的append函数,可以进行动态增长
newMonster := map[string]string{
"name": "猪八戒",
"age": "200",
}
monsters = append(monsters,newMonster)
fmt.Println(monsters)
}
7. map排序
1)基本介绍
<1> golang中没有一个专门的方法针对对map的key进行排序
<2> golang中的map默认是无序的,注意:也不是按照添加的顺序存放的,每次遍历得到的输出可能不一样.【案例演示】
<3> golang中map的排序,是先将key进行排序,然后根据key值遍历输出即可
package main
import (
"fmt"
"sort"
)
// 1)golang中没有一个专门的方法针对map的key进行排序
// 2)golang中的map默认是无序的,也不是按照添加的顺序存放的,每次遍历,得到的输出可能不一样
// 3)golang中的map排序,是先将key进行排序,然后根据key值遍历输出即可
func main() {
// map的排序
map1 := make(map[int]int, 10)
map1[10] = 100
map1[1] = 13
map1[4] = 56
map1[8] = 90
fmt.Println(map1)
keys := make([]int,0)
for k1,_ := range map1 {
keys = append(keys,k1)
}
sort.Ints(keys)
fmt.Println(keys)
for _,v1 := range keys {
fmt.Printf("map1[%v]=%v \n",v1,map1[v1])
}
}
8. map的使用细节
1)map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map【案例演示】
package main
import (
"fmt"
)
func modify(map1 map[int]int) {
map1[10] = 20
}
func main() {
// 1. map是引用类型,遵守引用类型传递的机制,在一个函数接收map
// 修改后,会直接修改原来的map
map1 := make(map[int]int)
map1[1] = 90
map1[2] = 88
map1[10] = 1
map1[20] = 2
modify(map1)
// 看看结果 map1[10] = 20,说明map是引用类型
fmt.Println(map1)
}
2)map的容量达到后,再向map增加元素,会自动扩容,不会发生panic,也就是说map能够动态增长 键值对(key-value)
3)map的value也经常使用struct类型,更适合管理复杂的数据(比前面value是一个map更好)
比如value为Student结构体【案例演示,因为还没学结构体,体验一下即可】
package main
import (
"fmt"
)
type Stu struct {
Name string
Age byte
Address string
}
func main() {
students := make(map[string]Stu)
// 创建两个学生
stu01 := Stu{"tom", 18, "北京"}
stu02 := Stu{"jack", 20, "上海"}
students["no1"] = stu01
students["no2"] = stu02
// 遍历各个学生的信息
for k1,v1 := range students {
fmt.Printf("学生的编号是:%v\t",k1)
fmt.Printf("学生的姓名是:%v\t",v1.Name)
fmt.Printf("学生的年龄是:%v\t",v1.Age)
fmt.Printf("学生的地址是:%v\t",v1.Address)
}
}
首先在append之后都要判断一下是否超容,如果超容,则新数组的容量首先扩大为原来的两倍。另外还有一条规律,当扩容之后再次比较发现容量还是不够用,就只会补够到相对应的容量数(当然前提还是要偶数