Go语言中的复数类型——Map详解

发表时间: 2023-09-27 11:43

Go语言中的map(映射、字典)是一种内置的数据结构, 它是一个无序的key-value对的集合, 比如以身份证号作为唯一键来标识一个人的信息。

info := map[int]string{

110: "mike",

111: "yoyo",

112: "lily"

}

key value

110 -> "mike",

111 -> "yoyo",

112 -> "lily"

map格式为:

map[keyType]valueType

在一个map里所有的健都是唯一的, 而且必须是支持==和!=操作符的类型, 切片、函数以及包含切片的结构类型, 这些类型由于具有引用语义, 不能作为映射的键, 使用这些类型会造成编译错误:

dict := map[[]string]int{} //err, invalid map key type []string

map值可以是任意类型, 没有限制。 map里所有定义的键和值的数据类型, {}括号必须遵循其定义数据类型的规范

注意: map是无序的, 我们无法决定它的返回顺序, 所以, 每次打印结果的顺序有可能不同

创建和初始化

map的创建

package main //必须有个main包import (    "fmt")func main() {    var m1 map[int]string //只是申明一个map,没有初始化,此为空(nil)map    fmt.Println(m1 == nil) //true    //m1[1] = "mike" //err, panic: assignment to entry in nil map    //解决方法: var m1 map[int]string = map[int]string{1: "lily"}    /*    或者 m1 = map[int]string{}    m1[1] = "mike"    */    //m2, m3的创建方法是等价的    m2 := map[int]string{}    m3 := make(map[int]string)    m2[1] = "mary" //此时可以赋值    fmt.Println(m2, m3) //map[1:mary] map[]    m4 := make(map[int]string, 10)    fmt.Println(m4)}

初始化

package main //必须有个main包import(    "fmt")func main(){    //1 定义同时初始化    var m1 map[int]string = map[int]string{1: "make", 2: "yoyo"}    fmt.Println(m1) //map[1:make 2:yoyo]    //2 自动推导类型 :=    m2 := map[int]string{1: "mike", 2: "yoyo"}    fmt.Println(m2)}

使用new和make创建map时的差异

在go语言中, 可以使用new和make来创建map类型的变量, 但是它们之间有很明显的不同, 使用new来创建map时, 返回的内容是一个指针, 这个指针指向了一个所有字段全为0的值map对象,

需要初始化后才能使用, 而使用make来创建map时, 返回的内容是一个引用, 可以直接使用。

使用new来创建并使用map:

//使用new创建一个map指针ma := new(map[string]int)//第一种初始化方法*ma = map[string]int{}(*ma)["a"] = 44fmt.Println(*ma)//第二种初始化方法*ma = make(map[string]int, 0)(*ma)["b"] = 55fmt.Println(*ma)//第三种初始化方法mb := make(map[string]int, 0)mb["c"] = 66*ma = mb(*ma)["d"] = 77fmt.Println(*ma)

使用make来创建并使用map:

ma := make(map[string]int)ma["a"] = 33fmt.Println(ma)

map的容量

package mainfunc main() {m := make(map[string]int, 99)cap(m) //error cap()内置函数不能用于map}

# command-line-arguments

./hello.go:5: invalid argument m (type map[string]int) for cap

实例:

package mainimport (    "fmt")func main() {    testMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New Delhi"}    //输出testMap长度    fmt.Println(len(testMap)) // 4}

先来看一下go的内置函数cap与map:

cap: 返回的是数组切片分配的空间大小, 根本不能用于map

make: 用于slice, map, 和channel的初始化

要获取map的容量, 可以用len函数。

常用操作

赋值

package main //必须有个main包import(    "fmt")func main(){    m1 := map[int]string{1: "make", 2: "yoyo"}    m1[1] = "xxx" //修改    m1[3] = "lily" //追加, go底层会自动为map分配空间    fmt.Println(m1) //map[3:lily 1:xxx 2:yoyo]    m2 := make(map[int]string, 10) //创建map    m2[0] = "aaa"    m2[1] = "bbb"    fmt.Println(m2) //map[0:aaa 1:bbb]    fmt.Println(m2[0], m2[1]) //aaa bbb}

判断某个键是否存在

Go语言中有个判断map中键是否存在的特殊写法, 格式如下:

value, ok := map[key]

package mainimport (    "fmt")// 遍历字符串func main() {    scoreMap := make(map[string]int)    scoreMap["张三"] = 90    scoreMap["小明"] = 100    // 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值    v, ok := scoreMap["张三"]    if ok {        fmt.Println(v)    } else {        fmt.Println("查无此人")    }}

简化写法:

package mainimport (    "fmt")// 遍历字符串func main() {    scoreMap := make(map[string]int)    scoreMap["张三"] = 90    scoreMap["小明"] = 100    // 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值    // 简化写法:    if v, ok := scoreMap["张三"]; ok {        fmt.Println(v)    } else {        fmt.Println("查无此人")    }}

遍历

package main //必须有个main包import(    "fmt")func main(){    m1 := map[int]string{1: "mike", 2: "yoyo"}    //迭代遍历1, 第一个返回值是key, 第二个返回值是value    for k, v := range m1 {    fmt.Printf("%d ---> %s\n", k, v)    }    /*    1 ---> mike    2 ---> yoyo    */    //迭代遍历2, 第一个返回值是key, 第二个返回值是value(可省略)    for k := range m1 { //也可以写成 k, _ := range m1    		fmt.Printf("%d ---> %s\n", k, m1[k])    }    /*    1 ---> mike    2 ---> yoyo    */    //判断某个key所对用的value是否存在, 第一个返回值是value(如果存在的话)    value, ok := m1[1]    fmt.Println("value = ", value, ", ok = ", ok) //value = mike , ok = true    value2, ok2 := m1[3]    fmt.Println("value2 = ", value2, ", ok2 = ", ok2) //value2 = , ok2 = false}

删除

package main //必须有个main包import(		"fmt")func main(){    m1 := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}    for k, v := range m1 {    		fmt.Printf("%d ----> %s\n", k, v)    }    delete(m1, 2) //删除key值为2的map    fmt.Println("------")    for k, v := range m1 {    		fmt.Printf("%d ----> %s\n", k, v)    }}

注意: delete:用于在 map 中删除实例, 注意: 只能删除 map 数据类型, 不能删除其他数据类型。

遍历map修改元素、删除所有元素

package mainimport (    "fmt")func main() {    var m map[int]string = map[int]string{1: "make", 2: "yoyo"}    // 修改元素的方法    for i, v := range m {        if v == "yoyo" {            m[i] = "xuchenkai" // 正确的修改方法, Go 和 PHP 语法一致            fmt.Println(i, v)        }    }    fmt.Println(m)    // 删除元素的方法    for k := range m {    		delete(m, k) // 循环遍历并删除map所有元素, 好处是map缓存还在, 下次添加时可直接使用缓存    }    fmt.Println(m)}

map作函数参数

在函数间传递映射并不会制造出该映射的一个脚本, 不是值传递, 而是引用传递。

package main //必须有个main包import(    "fmt")func DeleteMap(m map[int]string, key int){    for k, v := range m {    fmt.Printf("len(m) = %d, %d ---> %s\n", len(m), k, v)    }    delete(m, key)}func main(){    m := map[int]string{1: "mike", 2: "yoyo", 3: "lily"}    DeleteMap(m, 2) //删除key值为2的map    for k, v := range m {    fmt.Printf("len(m) = %d, %d ---> %s\n", len(m), k, v)    }    /*    len(m) = 3, 1 ---> mike    len(m) = 3, 2 ---> yoyo    len(m) = 3, 3 ---> lily    len(m) = 2, 1 ---> mike    len(m) = 2, 3 ---> lily    */}

实战:

map按照key, value进行排序

1. go语言的map是无序的, 多次遍历map的结果可能是不同的

举例如下:

package mainimport ("fmt")// GetMap 得到mapfunc GetMap() (result map[int]uint32) {    result = map[int]uint32{}    // 压入各个数据    result[24] = 223    result[17] = 91    result[9] = 13    result[11] = 330    result[55] = 100    return}func main() {    mapResu := GetMap()    // 遍历map    for key, value := range mapResu {    		fmt.Printf("key = %v,value = %v\n", key, value)    }}

第一次遍历结果如下:

key = 17,value = 91

key = 9,value = 13

key = 11,value = 330

key = 55,value = 100

key = 24,value = 223

第二次遍历结果如下:

key = 55,value = 100

key = 24,value = 223

key = 17,value = 91

key = 9,value = 13

key = 11,value = 330

可以看到两次遍历的结果是不同的

实现map遍历有序

1. key有序

思路:对key排序, 再遍历key输出value

代码如下:既可以从小到大排序, 也可以从大到小排序

package mainimport (    "fmt"    "sort")// GetMap 得到map及其所有的key// keys : map中所有的key,已排序,从小到大func GetMap() (result map[int]uint32, keys []int) {    result = map[int]uint32{}    keys = []int{}    // 压入各个数据    result[24] = 223    result[17] = 91    result[9] = 13    result[11] = 330    result[55] = 100    // 得到各个key    for key := range result {    keys = append(keys, key)    }    // 给key排序,从小到大    sort.Sort(sort.IntSlice(keys))    // 给key排序,从大到小    //sort.Sort(sort.Reverse(sort.IntSlice(keys)))    return}func main() {    mapResu, keys := GetMap()    // 注意:遍历keys,而不是遍历map    for _, key := range keys {    		fmt.Printf("key = %v,value = %v\n", key, mapResu[key])    }}

打印结果如下:

key = 9,value = 13

key = 11,value = 330

key = 17,value = 91

key = 24,value = 223

key = 55,value = 100

2. value有序

思路是直接不用map, 用struct存放key和value, 实现sort接口, 就可以调用sort.Sort进行排序了

package mainimport (    "fmt"    "sort")func main() {    mapInfo := map[string]int32{    "roy": 18,    "kitty": 16,    "hugo": 21,    "tina": 35,    "jason": 23,    }    type peroson struct {        Name string        Age int32    }    var lstPerson []peroson    for k, v := range mapInfo {    		lstPerson = append(lstPerson, peroson{k, v})    }    sort.Slice(lstPerson, func(i, j int) bool {        return lstPerson[i].Age > lstPerson[j].Age // 降序        // return lstPerson[i].Age < lstPerson[j].Age // 升序    })    fmt.Println(lstPerson)}

实战: 寻找最长不含有重复字符的子串

nonrepeating.go

package mainimport ("fmt")func lengthOfNonRepeatingSubStr(s string) int {    lastOccurred := make(map[rune]int)    start := 0    maxLength := 0    for i, ch := range []rune(s) {    if lastI, ok := lastOccurred[ch]; ok && lastI >= start {    		start = lastI + 1    }    if i-start+1 > maxLength {    		maxLength = i - start + 1    }    lastOccurred[ch] = i    }    return maxLength}func main() {    fmt.Println(    lengthOfNonRepeatingSubStr("abcabcbb"))    fmt.Println(    lengthOfNonRepeatingSubStr("bbbbb"))    fmt.Println(    lengthOfNonRepeatingSubStr("pwwkew"))    fmt.Println(    lengthOfNonRepeatingSubStr(""))    fmt.Println(    lengthOfNonRepeatingSubStr("b"))    fmt.Println(    lengthOfNonRepeatingSubStr("abcdef"))    fmt.Println(    lengthOfNonRepeatingSubStr("这里是慕课网"))    fmt.Println(    lengthOfNonRepeatingSubStr("一二三二一"))    fmt.Println(    lengthOfNonRepeatingSubStr(    "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"))}

实例: map如何判断key是否存在

判断方式为value,ok := map[key], ok为true则存在

package mainimport "fmt"func main() {    demo := map[string]int{    "a": 10,    }    fmt.Println(demo["a"]) // 10    //正确判断方法    _, ok := demo["a"]    fmt.Println(ok) //true    num, ok := demo["b"]    num += 1 //这里遵循零值可用    fmt.Println(num, ok) //1 false}