06-Go语言基础之map
map是key-value类型数据结构,读作(哈希表、字典),是一堆未排序的键值对集合。
map是引用类型,使用make函数或者初始化表达式创建。
map的key必须是支持相等运算符
==、!=的类型,如int、bool、channel、string、pointer、array、sruct、interface。通常map的key是int、string
map的value可以是任意类型,没有限制,通常是int、float、string、struct
map定义语法
map[keyType]ValueType
map类型的变量,由于是引用类型,默认值是nil,需要用make()初始化内存空间,语法
make(map[KeyType]ValueType,[cap]) 可选容量参数
map初始化
package main
import "fmt"
func main() {
/*
map声明语法
var 变量名 map[keytype]valuetype
var m1 map[string]string
var m2 map[int]string
var m3 map[int]map[string]string //map的value又是map
注意map声明不会分配内存,必须make初始化才可以使用
*/
// map也是引用类型
var initMap map[int]string
fmt.Printf("initMap类型:%T、值:%#v\n", initMap, initMap)
fmt.Println(initMap == nil)
//make分配内存之后,就不是nil了
initMap = make(map[int]string)
fmt.Println(initMap == nil)
//声明map方式一
//make函数可以合理申请一大块内存,避免后续扩张时性能问题
var m1 map[string]string
//标注map的初始容量为10
m1 = make(map[string]string, 10)
m1["一号"] = "大狗子"
m1["二号"] = "二狗子"
fmt.Printf("m1类型:%T、值:%#v\n", m1, m1)
//声明方式二,先开辟内存,然后单独赋值
m2 := make(map[string]string)
m2["男"] = "小黑"
m2["女"] = "小女"
fmt.Printf("m2类型:%T、值:%#v\n", m2, m2)
//声明方式三,最简写法,定义且赋值
m3 := map[string]string{
"坦克": "德玛西亚",
"射手": "伊泽瑞尔",
"辅助": "星女",
}
//添加新的 key:value
m3["打野"] = "赵信"
fmt.Printf("m3类型:%T、值:%#v\n", m3, m3)
}
/*
initMap类型:map[int]string、值:map[int]string(nil)
true
false
m1类型:map[string]string、值:map[string]string{"一号":"大狗子", "二号":"二狗子"}
m2类型:map[string]string、值:map[string]string{"女":"小女", "男":"小黑"}
m3类型:map[string]string、值:map[string]string{"坦克":"德玛西亚", "射手":"伊泽瑞尔", "打野":"赵信", "辅助":"星女"}
*/
map增删改查
对map的处理也就是围绕key、value,例如
- 判断map中是否存在某个key
- map遍历
- map增删改查
判断key是否存在
- map推荐用一种
ok特殊语法,判断key是否存在value,ok:=m1["k1"]
- map默认访问不存在的key,返回零值,不会发生错误
package main
import "fmt"
func main() {
m1 := map[string]string{"k1": "v1", "k2": "v2"}
fmt.Printf("m1值:%v\n", m1)
//访问不存在的key,也不会报错
fmt.Printf("kk存在吗:%#v\n", m1["kk"])
//map插入值
m1["k3"] = "v3"
m1["k4"] = "v4"
fmt.Printf("插入新key后m1值:%v\n", m1)
//map修改值
m1["k1"] = "v11111"
fmt.Printf("修改k1值后:%v\n", m1)
//map查找值
val, ok := m1["k4"]
if ok {
fmt.Printf("k4的值是:%v\n", val)
} else {
fmt.Println("查无此人!!")
}
//长度: 获取键值对数量
m1Len := len(m1)
fmt.Printf("m1有%d对数据\n", m1Len)
//判断key是否存在
if val, ok := m1["k4"]; !ok {
fmt.Printf("此key不存在\n")
} else {
fmt.Printf("此key的值:%v\n", val)
}
//删除map的key,如key不存在,delete不进行操作
//delete函数按指定的键,将元素从映射中删除
//一次性删除所有key可以遍历下,逐个删除
//也可以重新make新map,让原本map被gc回收
if _, ok := m1["k3"]; ok {
delete(m1, "k3")
fmt.Printf("已删除m1中的k3\n")
} else {
fmt.Printf("无法删除,此key不存在")
}
fmt.Printf("此时m1的值:%v\n", m1)
}
/*
m1值:map[k1:v1 k2:v2]
kk存在吗:""
插入新key后m1值:map[k1:v1 k2:v2 k3:v3 k4:v4]
修改k1值后:map[k1:v11111 k2:v2 k3:v3 k4:v4]
k4的值是:v4
m1有4对数据
此key的值:v4
已删除m1中的k3
此时m1的值:map[k1:v11111 k2:v2 k4:v4]
*/
map遍历
- map是无序的
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
//生成随机数种子
rand.Seed(time.Now().UnixNano())
//班里100个学生,随机生成每个学生的成绩
var stuMap = make(map[string]int, 100)
for i := 0; i < 100; i++ {
//字符串格式化学号
// %02d 生成如 00 01 02 03 04
k1 := fmt.Sprintf("stu%02d", i)
v1 := rand.Intn(100) //
stuMap[k1] = v1
}
fmt.Printf("stuMap类型:%T、值:%v\n", stuMap, stuMap)
//循环打印k、v
for k, v := range stuMap {
// %s 字符串格式化 %d整数
fmt.Printf("k--%s v--%d\n", k, v)
}
}
map排序后遍历
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func main() {
//生成随机数种子
rand.Seed(time.Now().UnixNano())
//班里100个学生,随机生成每个学生的成绩
var stuMap = make(map[string]int, 100)
//随机生成map k-v
for i := 0; i < 100; i++ {
//字符串格式化学号
// %02d 生成如 00 01 02 03 04
k1 := fmt.Sprintf("stu%02d", i)
v1 := rand.Intn(100) //
stuMap[k1] = v1
}
fmt.Printf("stuMap类型:%T、值:%v\n", stuMap, stuMap)
//排序map,提取出key,构造slice,然后排序
keys := make([]string, 0, 100)
for k := range stuMap {
keys = append(keys, k)
}
fmt.Printf("获取所有key的切片:%v\n", keys)
//对切片排序
sort.Strings(keys)
fmt.Printf("排序后的keys切片:%v\n", keys)
//根据有序的key,遍历map
for _, k := range keys {
fmt.Printf("k--%s、v--%d\n", k, stuMap[k])
}
}
复杂嵌套map
map的value、又是一个map
package main
import "fmt"
func main() {
/*
{
"stu01":{
"名字":"大狗子",
"年纪":"18"
}
}
可以理解为如此的嵌套json
*/
//make初始化第一层map,分配内存
stuMap := make(map[string]map[string]string)
//第二层map初始化
stuMap["stu01"] = make(map[string]string)
stuMap["stu01"]["名字"] = "大狗子"
stuMap["stu01"]["年纪"] = "18"
//切记,map必须make后方可使用
stuMap["stu02"] = make(map[string]string)
stuMap["stu02"]["名字"] = "二麻子"
stuMap["stu02"]["年纪"] = "17"
fmt.Printf("嵌套map类型:%T、值:%v\n", stuMap, stuMap)
//取出所有学生的信息
for k, v := range stuMap {
fmt.Printf("k值是学生:%v v值是学生信息:%v\n", k, v)
//k1是键,v1是值
for k1, v1 := range v {
fmt.Printf("\tk1:%v v1:%v\n", k1, v1)
}
fmt.Println()
}
}
元素为map类型的切片
- 首先主数据类型是slice
- 每一个元素是map
- 这样map的个数可以动态变化
package main
import "fmt"
func main() {
//初始化,还没有元素
var mapSlice = make([]map[string]string, 5)
// 初始slice、类型:[]map[string]string、值:[map[] map[] map[] map[] map[]]
fmt.Printf("初始slice、类型:%T、值:%v\n", mapSlice, mapSlice)
//切片元素创建,注意map需要初始化内存
//mapSlice[0] = make(map[string]string, 10)
//
//mapSlice[0]["name"] = "于超"
//mapSlice[0]["age"] = "18"
//fmt.Printf("赋值后slice、类型:%T、值:%v\n", mapSlice, mapSlice)
// 如果要批量多个元素赋值呢?,注意,每一个map都要初始化,因此可以for循环
//循环5个map元素
for i := 0; i < 5; i++ {
mapSlice[i] = make(map[string]string, 10)
//循环写入测试数据
for k := 0; k < 3; k++ {
k1 := fmt.Sprintf("k%d", k)
v1 := fmt.Sprintf("v%d", k)
mapSlice[i][k1] = v1
}
}
fmt.Printf("循环赋值后slice、类型:%T、值:%v\n", mapSlice, mapSlice)
}
/*
初始slice、类型:[]map[string]string、值:[map[] map[] map[] map[] map[]]
赋值后slice、类型:[]map[string]string、值:[map[age:18 name:于超] map[] map[] map[] map[]]
*/
map切片扩容
package main
import "fmt"
func main() {
//初始化,还没有元素
var mapSlice = make([]map[string]string, 5)
// 初始slice、类型:[]map[string]string、值:[map[] map[] map[] map[] map[]]
fmt.Printf("初始slice、类型:%T、值:%v\n", mapSlice, mapSlice)
//切片元素创建,注意map需要初始化内存
//mapSlice[0] = make(map[string]string, 10)
//
//mapSlice[0]["name"] = "于超"
//mapSlice[0]["age"] = "18"
//fmt.Printf("赋值后slice、类型:%T、值:%v\n", mapSlice, mapSlice)
// 如果要批量多个元素赋值呢?,注意,每一个map都要初始化,因此可以for循环
//循环5个map元素
for i := 0; i < 5; i++ {
mapSlice[i] = make(map[string]string, 10)
//循环写入测试数据
for k := 0; k < 3; k++ {
k1 := fmt.Sprintf("k%d", k)
v1 := fmt.Sprintf("v%d", k)
mapSlice[i][k1] = v1
}
}
fmt.Printf("循环赋值后slice、类型:%T、值:%v、元素个数:%d\n", mapSlice, mapSlice, len(mapSlice))
//mapSlice已满,需要扩容
newMap := map[string]string{
"new_k0": "new_v0",
"new_k1": "new_v1",
}
//依然是append扩容切片
mapSlice = append(mapSlice, newMap)
fmt.Printf("扩容后后slice、类型:%T、值:%v、元素个数:%d\n", mapSlice, mapSlice, len(mapSlice))
//循环取出每一个map
for i, v := range mapSlice {
fmt.Printf("元素%d:%v\n", i, v)
}
}
/*
初始slice、类型:[]map[string]string、值:[map[] map[] map[] map[] map[]]
循环赋值后slice、类型:[]map[string]string、值:[map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2]]、元素个数:5
扩容后后slice、类型:[]map[string]string、值:[map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[k0:v0 k1:v1 k2:v2] map[new_k0:new_v0 new_k1:new_v1]]、元素个数:6
元素0:map[k0:v0 k1:v1 k2:v2]
元素1:map[k0:v0 k1:v1 k2:v2]
元素2:map[k0:v0 k1:v1 k2:v2]
元素3:map[k0:v0 k1:v1 k2:v2]
元素4:map[k0:v0 k1:v1 k2:v2]
元素5:map[new_k0:new_v0 new_k1:new_v1]
*/
值为slice的map
- 数据类型是map
- value类型的是slice
package main
import (
"fmt"
)
func main() {
// 初始化map、值是slice
// 定义班级信息,班里10个人,分男女
/*
如下结构
{
"男孩":[
1,
2,
3
],
"女孩":[
1,
2,
3
]
}
*/
stuMap := make(map[string][]string, 2)
//男同学3个,女同学7个
k1 := "男孩"
value, ok := stuMap[k1]
if !ok {
fmt.Printf("该key不存在:%v、添加key中\n", value)
value = make([]string, 0, 3) //生成默认空元素切片
for i := 0; i < 3; i++ {
value = append(value, fmt.Sprintf("男孩%d", i))
stuMap[k1] = value
}
}
fmt.Printf("添加男孩后的map:%#v\n", stuMap)
//女孩添加7个 ,序号从04开始
k2 := "女孩"
v2, ok := stuMap[k2]
if !ok {
fmt.Printf("该key不存在:%v、添加key中\n", v2)
v2 = make([]string, 0, 7) //生成默认空元素切片
for i := 3; i < 10; i++ {
v2 = append(v2, fmt.Sprintf("女孩%d", i))
stuMap[k2] = v2
}
}
fmt.Printf("添加女孩后的map:%v\n", stuMap)
//取出班里所有学生,循环map
for k, v := range stuMap {
fmt.Printf("性别--%s、学生列表--%v\n", k, v)
}
}
/*
该key不存在:[]、添加key中
添加男孩后的map:map[string][]string{"男孩":[]string{"男孩0", "男孩1", "男孩2"}}
该key不存在:[]、添加key中
添加女孩后的map:map[女孩:[女孩3 女孩4 女孩5 女孩6 女孩7 女孩8 女孩9] 男孩:[男孩0 男孩1 男孩2]]
性别--男孩、学生列表--[男孩0 男孩1 男孩2]
性别--女孩、学生列表--[女孩3 女孩4 女孩5 女孩6 女孩7 女孩8 女孩9]
*/
map细节技巧
map函数传递
map由于是引用类型,遵循引用类型传参的机制,函数传参时接收的map、如果修改map参数,等于对源map修改。
package main
import "fmt"
// 函数接收map参数
func modify(m map[string]string) {
m["名字"] = "狗子"
}
func main() {
//map是引用类型,遵循引用引用类型传递
m1 := make(map[string]string)
m1["名字"] = "傻子"
m1["年纪"] = "十八"
fmt.Printf("源map:%v\n", m1)
modify(m1) //直接对m1进行修改,说明是引用类型
fmt.Printf("修改后的map:%v\n", m1)
}
/*
源map:map[名字:傻子 年纪:十八]
修改后的map:map[名字:狗子 年纪:十八]
*/
map扩容
map是自动扩容,动态增长
package main
import "fmt"
func main() {
//初始化m1,限制容量3
m1 := make(map[int]int, 3)
for i := 0; i < 10; i++ {
m1[i] = i + i
}
fmt.Println(m1)
fmt.Printf("m1元素个数:%v", len(m1))
}
map值为struct
package main
import "fmt"
type Stu struct {
Name string
Age int
Address string
}
func main() {
//map的value可以为更复杂的struct结构体类型
//map的key是学号
//map的value是结构体{姓名、年纪、住址}
students := make(map[int]Stu, 10)
//初始化结构体,不需要填写key,顺序value即可
stu1 := Stu{"超哥", 28, "沙河"}
stu2 := Stu{"孙悟空", 999, "花果山"}
students[1] = stu1
students[2] = stu2
fmt.Println(students)
//遍历map,取出学生信息
for k, v := range students {
fmt.Printf("学生编号%v\n", k)
fmt.Printf("学生姓名%v\n", v.Name)
fmt.Printf("学生年纪%v\n", v.Age)
fmt.Printf("学生住址%v\n", v.Address)
fmt.Println("--------")
}
}
/*
map[1:{超哥 28 沙河} 2:{孙悟空 999 花果山}]
学生编号1
学生姓名超哥
学生年纪28
学生住址沙河
--------
学生编号2
学生姓名孙悟空
学生年纪999
学生住址花果山
--------
*/
map练习题
统计单词出现次数
package main
import (
"fmt"
"strings"
)
func main() {
s1 := "i love you and you love me"
/*
构造map、key为单词、value为次数
*/
//strings.Split函数,返回切片
slice1 := strings.Split(s1, " ")
fmt.Printf("切片值:%#v\n", slice1)
//构造map
m1 := make(map[string]int)
for _, v := range slice1 {
// 挨个的如 m1["i"]=1
//num := m1[v]
//同一个k的值,进行+1统计
//m1[v] = num + 1
//出现一次就+1 ,最简写法
//m1[v]++
//啰嗦写法
num, ok := m1[v]
if ok {
m1[v] = num + 1 //再次出现,次数+1
} else {
m1[v] = 0 + 1 //首次统计是1次
}
}
for k, v := range m1 {
fmt.Printf("单词:%s \t 次数:%d\n", k, v)
}
}
/*
单词:love 次数:2
单词:you 次数:2
单词:and 次数:1
单词:me 次数:1
单词:i 次数:1
*/
阅读代码结果
这道题,就考察对切片的理解。
- 切片是引用类型,多个切片变量,引用同一个底层数组,修改则全变化。
- 只要切片的容量不发生扩容,就不会跟换底层数组。
package main
import (
"fmt"
)
func main() {
//自定义类型,Map ,值是切片
type Map map[string][]int
m := make(Map)
s := []int{1, 2} // [1,2]
fmt.Printf("源切片s:%#v、长度:%d、容量:%d\n", s, len(s), cap(s))
s = append(s, 3) // [1,2,3] 这里自动扩容了
fmt.Printf("扩容后切片s:%#v、长度:%d、容量:%d\n", s, len(s), cap(s))
m["yuchao"] = s // map赋值 k、v
//解题关键点,源map是 map[yuchao:[1 2 3]]
//
fmt.Printf("源map:%v\n", m)
s = append(s[:1], s[2:]...) //修改切片,等于修改底层数组
fmt.Printf("修改切片s后值:%#v、长度:%d、容量:%d\n", s, len(s), cap(s)) // [1,3],长度2,源map的值,
fmt.Printf("最终map的切片:%#v、长度:%d、容量%d\n", m["yuchao"], len(m["yuchao"]), cap(m["yuchao"]))
}
/*
源切片s:[]int{1, 2}、长度:2、容量:2
扩容后切片s:[]int{1, 2, 3}、长度:3、容量:4
源map:map[yuchao:[1 2 3]]
修改切片s后值:[]int{1, 3}、长度:2、容量:4
最终map的切片:[]int{1, 3, 3}、长度:3、容量4
*/