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}