Map
map是一种元素对的无序集合,一组称为元素value,另一组为唯一键索引key。 未初始化map的值为nil。map 是引用类型,可以使用如下声明:
var map1 map[keytype]valuetype
([keytype] 和 valuetype 之间允许有空格,但是 Gofmt 移除了空格)
在声明的时候不需要知道 map 的长度,map 是可以动态增长的。
map的底层存储方式为数组,在存储时key不能重复,当key重复时,value进行覆盖,我们通过key进行hash运算(可以简单理解为把key转化为一个整形数字)然后对数组的长度取余,得到key存储在数组的哪个下标位置,最后将key和value组装为一个结构体,放入数组下标处,看下图:
map定义
key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、函数、字典、切片和结构体不能作为 key (含有数组切片的结构体不能作为 key,只包含内建类型的 struct 是可以作为 key 的),但是指针和接口类型可以。
value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。map 也可以用函数作为自己的值,这样就可以用来做分支结构:key 用来选择要执行的函数。
map 传递给函数的代价很小:在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。
map基本使用
map 可以用 {key1: val1, key2: val2} 的描述方法来初始化,就像数组和结构体一样。
map 是引用类型的,内存用 make 方法来分配。map 的初始化:
var map1 = make(map[keytype]valuetype)
map初始化
map 容量: 和数组不同,map 可以根据新增的 key-value 对动态的伸缩,因此它不存在固定长度或者最大限制。但是你也可以选择标明 map 的初始容量 capacity,就像这样:make(map[keytype]valuetype,cap)。
例如:
map2 := make(map[string]float32, 100)
当 map 增长到容量上限的时候,如果再增加新的 key-value 对,map 的大小会自动加 1。所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。
在一个 nil 的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。
可正常运行:
package main
func main() {
var s []int
s = append(s, 1)
}
会发生错误:
package main
func main() {
var m map[string]int
m["one"] = 1 // 错误
}
map的key访问,val1, isPresent := map1[key1] 或者 val1 = map1[key1] 的方法获取 key1 对应的值 val1。
这里有一些定义 map 的例子:
// 声明但未初始化map,此时是map的零值状态
map1 := make(map[string]string, 5)
map2 := make(map[string]string)
// 创建了初始化了一个空的的map,这个时候没有任何元素
map3 := map[string]string{}
// map中有三个值
map4 := map[string]string{"a": "1", "b": "2", "c": "3"}
从 map1 中删除 key1,直接 delete(map1, key1) 就可以。如果 key1 不存在,该操作不会产生错误。
delete(map4, "a")
map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包)。
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": "pprof.cn",
"password": "123456",
}
fmt.Println(userInfo) //
}
判断某个键是否存在
Go语言中有个判断map中键是否存在的特殊写法,格式如下:
value, ok := map[key]
举个例子:
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("查无此人")
}
}
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时的元素顺序与添加键值对的顺序无关。
删除键值对
使用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)
}
}
按照指定顺序遍历map
func main() {
rand.Seed(time.Now().UnixNano()) //初始化随机数种子
var scoreMap = make(map[string]int, 200)
for i := 0; i < 100; i++ {
key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
value := rand.Intn(100) //生成0~99的随机整数
scoreMap[key] = value
}
//取出map中的所有key存入切片keys
var keys = make([]string, 0, 200)
for key := range scoreMap {
keys = append(keys, key)
}
//对切片进行排序
sort.Strings(keys)
//按照排序后的key遍历map
for _, key := range keys {
fmt.Println(key, scoreMap[key])
}
}
元素为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中值为切片类型的操作:
func main() {
var sliceMap = make(map[string][]string, 3)
fmt.Println(sliceMap)
fmt.Println("after init")
key := "中国"
value, ok := sliceMap[key]
if !ok {
value = make([]string, 0, 2)
}
value = append(value, "北京", "上海")
sliceMap[key] = value
fmt.Println(sliceMap)
}
Map的比较运算符
Go Map对值的类型没有限制,但是对键的类型有严格的要求,因为它必须保证键的唯一性。Go 要求键类型必须支持两个比较运算符==
和!=
。
需要注意的一点是,在 Go 中,function/map/slices 只支持和nil比较,它们不支持自己类型的比较,如果运行以下代码,您将看到无效操作错误:
package main
import "fmt"
func main() {
s1 := make([]int, 1)
s2 := make([]int, 2)
f1 := func() {}
f2 := func() {}
m1 := make(map[int]string)
m2 := make(map[int]string)
fmt.Println(s1 == s2)
fmt.Println(f1 == f2)
fmt.Println(m1 == m2)
}
// Output
invalid operation: s1 == s2 (slice can only be compared to nil)
invalid operation: f1 == f2 (func can only be compared to nil)
invalid operation: m1 == m2 (map can only be compared to nil)
排序
按键对map排序
对我的键进行排序相对容易,创建一个键集合并使用 golang 排序包sort.Strings进行排序。
package main
import (
"fmt"
"sort"
)
func main() {
population := map[string]int{
"Australia": 24982688,
"Qatar": 2781677,
"Wales": 3139000,
"Burundi": 11175378,
"Guinea": 12414318,
"Niger": 22442948,
"Brazil": 209469333,
"Malta": 484630,
"Peru": 31989256,
"Yemen": 28498687,
"Ireland": 4867309,
"Kenya": 51393010,
"Montserrat": 5900,
"Cuba": 11338138,
"Nicaragua": 6465513,
"Jordan": 9956011,
"Gabon": 2119275,
}
keys := make([]string, 0, len(population))
for k := range population {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, population[k])
}
}
按值对map进行排序
按值排序稍微复杂一些。最好的方法是创建一个键和值属性的结构。
创建的结构体应该实现golang 排序接口,使其能够进行排序
package main
import (
"fmt"
"sort"
)
type Pair struct {
Key string
Value int
}
type PairList []Pair
func (p PairList) Len() int { return len(p) }
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func main() {
population := map[string]int{
"Australia": 24982688,
"Qatar": 2781677,
"Wales": 3139000,
"Burundi": 11175378,
"Guinea": 12414318,
"Niger": 22442948,
"Brazil": 209469333,
"Malta": 484630,
"Peru": 31989256,
"Yemen": 28498687,
"Ireland": 4867309,
"Kenya": 51393010,
"Montserrat": 5900,
"Cuba": 11338138,
"Nicaragua": 6465513,
"Jordan": 9956011,
"Gabon": 2119275,
}
p := make(PairList, len(population))
i := 0
for k, v := range population {
p[i] = Pair{k, v}
i++
}
sort.Sort(p)
//p is sorted
for _, k := range p {
fmt.Printf("%v\t%v\n", k.Key, k.Value)
}
}
传参
关于传参,我们可以看 【go从入门到精通】函数详解-CSDN博客 这篇文章,那么大家思考下map传参是值传递还是引用传递呢?
标签:map,string,fmt,key,go,scoreMap,make,字典 From: https://blog.csdn.net/pbymw8iwm/article/details/136479907