概览
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。
key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、切片不能作为 key。含有数组切片的结构体不能作为 key,只包含内建类型的 struct 也可以作为 key ,但是指针和接口类型可以。
声明和初始化
dict := make(map[string]int)
创建一个映射,键的类型是 string,值的类型是 intdict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
创建一个映射,键和值的类型都是 string,使用两个键值对初始化映射var colors map[string]string
通过声明映射创建一个 nil 映射,注意:nil 映射不能用于存储键值对。这点跟切片不一样,nil 切片可以append元素。colors := map[string]string{}
创建一个空映射,用来存储颜色以及颜色对应的十六进制代码
使用映射
colors["Red"] = "#f00"
给映射赋值delete(colors, "Red")
从映射中删除一项,key 不存在,该操作不会产生错误。- 从映射获取值并判断键是否存在
if value, exists := colors["Red"]; exists { fmt.Println(value) } else { fmt.Println("value not exists") }
- 迭使用 range 迭代映射
for key, value := range colors { fmt.Printf("Key: %s Value: %s\n", key, value) }
在函数间传递映射
在函数间传递映射并不会制造出该映射的一个副本。当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。map 传递给函数的代价很小:在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。
一对多
一个 key 只能对应一个 value,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办。可以将值设为切片类型。如:
package main
import (
"fmt"
)
type M map[string]interface{}
func main() {
mapSlice := make(M)
list := []M{M{"1": "12"}, M{"1": "123"}, M{"2": "1234"}}
for _, oneM := range list {
for key, value := range oneM {
if ik, ok := mapSlice[key].([]interface{}); ok { //ik为mapSlice[key]的拷贝
fmt.Printf("0 %p\n", &ik)
ik = append(ik, value) //此时ik切片指向的底层数组的内存地址已经发生的变化,ik本身的地址并未变化
fmt.Printf("0 %p\n", &ik)
mapSlice[key] = ik //所以这里需要赋值
} else {
tmp := []interface{}{value}
mapSlice[key] = tmp
}
}
}
fmt.Printf("%#v", mapSlice)
}