11 Go语言的映射——map
Go语言的映射——map
[TOC]
- 类似其它语言中的哈希表或者字典,以key-value形式存储数据
- Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice;value 可以是任意类型。
- Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍
- Map使用make()创 建,支持 := 这种简写方式
- make(map[keyType]valueType, cap),cap表示容量,可省略
- 超出容量时会自动扩容,但尽量提供一个合理的初始值,预先申请内存,有助于提升性能
- 使用len()获取元素个数
- 键值对不存在时自动添加,使用delete()删除某键值对
- 使用 for range 对map和slice进行迭代操作
Map介绍
go语言中的map是一种数据结构,用于存储一些列无序的键值对。map是一个无序的集合,因为它底层是一个hash表(散列表)。map的键只要支持==或!=比较运算的类型都可以,但是不可以是函数,map,slice等不能比较的。value可以是任意的类型。
1、Map 的创建
1.1 使用make函数
// 创建一个映射,键的类型是 string,值的类型是 int dict := make(map[string]int)
make(map[keyType]valueType, cap),cap表示容量,可以在创建的时候指定一个合理初始的容量大小,这样就会申请一大块内存,避免在后续使用中频繁扩张浪费性能。比如:m := make(map[string]int, 1000)
1.2 使用字面值创建
// 创建一个映射,键和值的类型都是 string // 使用两个键值对初始化映射 dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
2、使用映射
2.1为映射赋值
// 创建一个空映射,用来存储颜色以及颜色对应的十六进制代码 colors:=map[string]string{} // 将 Red 的代码加入到映射 colors["Red"]="#da1337"
2.1 未初始化的map,赋值报错,即 nil map
// 通过声明映射创建一个 nil 映射 var colors map[string]string // 将 Red 的代码加入到映射 colors["Red"] = "#da1337" Runtime Error: panic: runtime error: assignment to entry in nil map
2.2 从map 中获取值,并判断键是否存在
由于go的多返回值,map获取值的时候,会返回值,和一个boolean的参数,表示成不成功,有没有,对不对。
在 Go 语言里,通过键来索引映射时,即便这个键不存在也总会返回一个值。在这种情况下,返回的是该值对应的类型的零值。
// 获取键 Blue 对应的值 value, exists := colors["Blue"]// 这个键存在吗? if exists { fmt.Println(value) } //当然了也可以直接用 类型的零值来判断----是一样的 // 获取键 Blue 对应的值 value := colors["Blue"] // 这个键存在吗? if value != "" { fmt.Println(value) }
2.3 用range迭代map
// 创建一个映射,存储颜色以及颜色对应的十六进制代码 colors := map[string]string{ "AliceBlue": "#f0f8ff", "Coral": "#ff7F50", "DarkGray": "#a9a9a9", "ForestGreen": "#228b22", } // 显示映射里的所有颜色 for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value) } //
range迭代和数组、slice 都一样,只不过这里返回的是map的键值对,而array ,slice返回的是索引和值
2.4 map中的delete函数
delete(map,key) 函数可以从map中删除指定key的键值对。这种方法只能用在映射存储的值都是非零值的情况
// 删除键为 Coral 的键值对 delete(colors, "Coral") // 显示映射里的所有颜色 for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value) }
2.5 map作为参数传递
和slice一样,都是引用类型,都是指向了底层数据结构。slice指向的是数组,map指向的是hash表。map在函数之间作为参数传递的时候,是进行map指针的拷贝,相对于指针来说是值拷贝,相对于底层来说是引用传递。 其实我觉得go所有的传递都是值传递,只不过有的值是值,有的值是指针。
所以,在函数中传递map,对map进行修改会对底层数据进行修改。
// removeColor 将指定映射里的键删除 func removeColor(colors map[string]string, key string) { delete(colors, key) }
// 创建一个映射,存储颜色以及颜色对应的十六进制代码 colors := map[string]string{ "AliceBlue": "#f0f8ff", "Coral": "#ff7F50", "DarkGray": "#a9a9a9", "ForestGreen": "#228b22", } // 显示映射里的所有颜色 for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value) } fmt.Println() // 调用函数来移除指定的键 removeColor(colors, "Coral") // 显示映射里的所有颜色 for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value) } //输出结果 Key: AliceBlue Value: #F0F8FF Key: Coral Value: #FF7F50 Key: DarkGray Value: #A9A9A9 Key: ForestGreen Value: #228B22 Key: AliceBlue Value: #F0F8FF Key: DarkGray Value: #A9A9A9 Key: ForestGreen Value: #228B22
map的常见操作
m := map[string]int{ "a": 1, } if v, ok := m["a"]; ok { // 判断 key 是否存在。 println(v) } println(m["c"]) // 对于不存在的 key,直接返回 \0,不会出错。 m["b"] = 2 // 新增或修改。 delete(m, "c") // 删除。如果 key 不存在,不会出错。 println(len(m)) // 获取键值对数量。 cap ⽆效。 for k, v := range m { // 迭代,可仅返回 key。随机顺序返回,每次都不相同。 println(k, v) }
3、从map取出的value是一个拷贝,对其成员修改没有意义
type user struct{ name string } m := map[int]user{ // 当 map 因扩张⽽重新哈希时,各键值项存储位置都会发⽣改变。 因此, map 1: {"user1"}, // 被设计成 not addressable。 类似 m[1].name 这种期望透过原 value } // 指针修改成员的⾏为⾃然会被禁⽌。 m[1].name = "Tom" // Error: cannot assign to m[1].name
因为取出的是一个user实例的拷贝,不能直接对其成员修改,如果要实现,可以有两种方式
3.1完整的替换这个value
u := m[1] u.name = "Tom" m[1] = u // 替换 value。
3.2 使用指针
m2 := map[int]*user{ 1: &user{"user1"}, } m2[1].name = "Jack" // 返回的是指针复制品。透过指针修改原对象是允许的。