Go语言学习13-map
0x00 定义
映射(map),Go语言中内置的一种类型,它将键值对相关联,我们可以通过键key来获取对应的值value。类似其他语言的集合。
map是一种无序的基于key-value
的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
键值对:一对匹配的信息
Go语言中 map
的定义语法如下:
map[KeyType]ValueType
其中,
- KeyType:表示键的类型。
- ValueType:表示键对应的值的类型。
map类型的变量默认初始值为nil,需要使用make()函数来分配内存。语法为:
make(map[KeyType]ValueType, [cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。
PS:key、value类型:bool、数字、string、指针、channel、还可以是只包含前面几个类型的接口、结构体、数组
PS:key通常为int、string类型、value通常为数字(整数、浮点数)、string、map、结构体
PS:slice、map、function不可
0x01 map基本使用
map中的数据都是成对出现的,map的基本使用示例代码如下:
func main() {
scoreMap := make(map[string]int, 8) //一次性定义好容量,有利于程序的快捷
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)
}
输出:
map[小明:100 张三:90]
100
type of a:map[string]int
map也支持在声明的时候填充元素,例如:
func main() {
userInfo := map[string]string{
"username": "沙河小王子",
"password": "123456",
}
fmt.Println(userInfo) //
}
0x02 判断某个键是否存在
Go语言中有个判断map中键是否存在的特殊写法,格式如下:
value, ok := map[key]
举个例子:
func main() {
var m1 map[string]int //这是map初始化
//fmt.Println(m1 == nil) 返回的是一个true,说明此为空,还未初始化,在内存中没有开辟空间
m1 = make(map[string]int, 10) //可以自动扩容,但是初始化的时候最好估算好该map的容量,避免在程序运行期间再动态扩容
//10就是键值对的数量
m1["beijing"] = 1
m1["shanghai"] = 2
m1["jiwuming"] = 35
fmt.Println(m1)
fmt.Println(m1["beijing"])
value, ok := m1["nihao"] //约定成俗,用ok接收别的返回的bool值!如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
if !ok {
fmt.Println("查無此人!")
} else {
fmt.Println(value)
}
}
0x03 map的遍历
Go语言中使用for range
遍历map。
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
fmt.Println(k, v)
}
}
但我们只想遍历key的时候,可以按下面的写法:
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k := range scoreMap {
fmt.Println(k)
}
}
注意: 遍历map时的元素顺序与添加键值对的顺序无关。
0x03 特点
1、map集合在使用前一定要make
2、map的key-value是无序的
3、key是不可以重复的,如果遇到重复,后一个value会覆盖之前的
4、value是可以重复的
【4】三种map创建方式
方式一:var然后make函数(不推荐,了解)
var a map[int]string
//只声明map,内存是没有分配空间的
//必须通过make函数进行初始化才能分配空间
a = make(map[int]string, 10) //map可以存放10个键值对
a[2000] = "张三"
a[2001] = "李四"
方式二:推断
b := make(map[int]string)
b[1] = "beijing"
b[2] = "shanghai"
b[3] = "guagndong"
方式三:类似json格式的
c := map[int]string{
1 : "beijing",
2 : "shanghai",
3 : "guangdong", //一定要注意逗号,千万不能忘掉
}
【5】map的增删改
增加与修改
使用delete()函数删除键值对
使用delete()
内建函数从map中删除一组键值对,delete()
函数的格式如下:
delete(map, key)
其中,
- map:表示要删除键值对的map
- key:表示要删除的键值对的键
示例代码如下:
func main(){
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明")//将小明:100从map中删除
for k,v := range scoreMap{
fmt.Println(k, v)
}
}
delete(b, 2)
delete(b, 5)
fmt.Println(b)
注意:清空操作
1、删除map中的所有key,没有一个专门的方法一次性删除,可以遍历一下key,逐个删除;或者手动一个一个删除
2、不过上面这个方法过于麻烦,所以我们怎么做呢,就是通过map = make(...),make一个新的,让原来的成为垃圾,被gc回收
查找
value, flag := b[4]
fmt.Println(value, flag)
按照指定顺序遍历map
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())//当前时间的纳秒输出,并且随机化
scoreMap := make(map[string]int) //新建一个map
for i := 0; i < 100; i++ {
key := fmt.Sprintf("Stu%02d", i) //for循环输出99个学生名称,并赋予给key变量
value := rand.Intn(100) //随机生成1-100赋值给value变量
scoreMap[key] = value //将scoreMap的key和value一一对应起来
}
// fmt.Println(scoreMap)
//将结果打印到切片中
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//随后对切片进行排序
sort.Strings(keys)
//按照排序后的keys进行遍历
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
map与slice结合
map和slice都要初始化
下面这段代码,可以看到,make函数制作了一个切片,这个切片是map[int]string类型的。下方这个panic错误是因为make了一个切片,没有任何的数值,所以我们需要新建一个数值,来保证不会显示索引越界错误。
func main() {
m1 := make([]map[int]string, 0, 10)
m1[0][100] = "beijing"
fmt.Println(m1)
}
当我将make函数里面的长度变成1,就会显示nil,为空,是因为map并没有初始化,也就是赋值之类的。所以需要对map进行初始化。
func main() {
m1 := make([]map[int]string, 0, 10)
m1[0][100] = "beijing"
fmt.Println(m1)
}
对map进行初始化
func main() {
m1 := make([]map[int]string, 10, 10)
m1[0] = make(map[int]string, 1) //m1第一个map进行初始化
m1[0][10] = "beijing" //m1第一个map的值赋值,因为一共有10个map,剩余9个就默认是空值
fmt.Println(m1)
}
元素为map类型的切片
下面的代码演示了切片中的元素为map类型时的操作:
func main() {
var mapSlice = make([]map[string]string, 3)
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("after init")
// 对切片中的map元素进行初始化
mapSlice[0] = make(map[string]string, 10)
mapSlice[0]["name"] = "小王子"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "沙河"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
值为切片类型的map
啥意思?就是平时我们新建map的时候,是make(map[int]string)
,这回把string
替换成[]string
切片类型。
m2 := make(map[string][]string, 10)
m2["beijing"] = []string{"朝阳区", "海淀区", "东城区", "房山区", "昌平区"}
m2["shanghai"] = []string{"上海外滩", "莆田区", "老八区"}
fmt.Println(m2)
总结
举上面这两个例子的目的是为了提醒,map和切片在定义后,一定要初始化!
练习题
-
写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。
思路总结一下:
- 先字符串切割成切片,然后遍历这些切片到map
- 再判断每个单词是否在map中
- 累计一下每个单词出现的次数
-
观察下面代码,写出最终的打印结果。
func main() {
type Map map[string][]int //定义Map的类型为 map[string][]int,也就是值为切片的map类型
m := make(Map) //初始化一个Map,赋予变量给m
s := []int{1, 2} //s定义为一个具有两个值的切片
s = append(s, 3) //添加一个数值,即s = []int{1,2,3}
fmt.Printf("%+v\n", s) //打印s
m["q1mi"] = s //m的qlmi键对应值为s
s = append(s[:1], s[2:]...) //删除s[1]=2
fmt.Printf("%+v\n", s) //s={1,3}
fmt.Printf("%+v\n", m["q1mi"]) //m["qlmi"]=[]int{1,2,3}
}