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

*/

Copyright © www.yuchaoit.cn 2025 all right reserved,powered by Gitbook作者:于超 2022-12-26 16:43:30

results matching ""

    No results matching ""