Map
字典和前面我们所说的数组,链表的不同在于它能存储的不是单一值的集合,而是键值对的集合。
声明方法
var map1 map[keytype]valuetype
代码实例
aMap := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
//按Key取值
k := "two"
v, ok := aMap[k]
if ok {
fmt.Printf("The element of key %q: %d\n", k, v)
} else {
fmt.Println("Not found!")
}
//遍历map
for k, v := range myMap {
fmt.Println(k, v)
}
//存储K-V
myMap := make(map[string]string, 10)
myMap["a"] = "b"
//value可以是接口
myFuncMap := map[string]func() int{
"funcA": func() int { return 1 },
}
fmt.Println(myFuncMap)
f := myFuncMap["funcA"]
fmt.Println(f())
Map的访问流程
- 哈希表会先用哈希函数(hash function)把键值转换为哈希值。哈希值通常是一个无符号整数。一个哈希表会持有一定数量哈希桶,这些哈希桶会均匀地储存其所属哈希表收纳的键 - 元素对。
- 哈希表会先用这个键哈希值的低几位去定位到一个哈希桶,然后再去这个哈希桶中,查找这个键。
- 一旦找到了键,就一定能找到对应的元素值。随后,哈希表就会把相应的元素值作为结果返回。
Map使用的注意事项
1. Key类型的约束
Go 语言规范规定,在键类型的值之间必须可以施加操作符==和!=, 由于函数类型、字典类型和切片类型的值并不支持判等操作,所以字典的键类型不能是这些类型。
代码实例
//编译不会出错
var badMap2 = map[interface{}]int{
"1": 1,
[]int{2}: 2, // 这里会引发 panic。
3: 3,
}
//Go 语言的运行时(runtime)系统就会发现这里的问题,它会抛出一个 panic
2.应该优先考虑哪些类型作为字典的键类型?
求哈希和判等操作的速度越快,对应的类型就越适合作为键类型。
基本类型
求哈希的操作为例,宽度越小的类型速度通常越快。对于布尔类型、整数类型、浮点数类型、复数类型和指针类型来说都是如此。对于字符串类型,由于它的宽度是不定的,所以要看它的值的具体长度,长度越短求哈希越快。
类型的宽度是指它的单个值需要占用的字节数。比如,bool、int8和uint8类型的一个值需要占用的字节数都是1,因此这些类型的宽度就都是1。
高级类型
- 对数组类型的值求哈希实际上是依次求得它的每个元素的哈希值并进行合并,所以速度就取决于它的元素类型以及它的长度。细则同上。
- 对结构体类型的值求哈希实际上就是对它的所有字段值求哈希并进行合并,所以关键在于它的各个字段的类型以及字段的数量。
- 对于接口类型,具体的哈希算法,则由值的实际类型决定。
总结
- 不建议使用高级数据类型作为字典的键类型,不仅仅是因为对它们的值求哈希,以及判等的速度较慢,更是因为在它们的值中存在变数,
- 优先选用数值类型和指针类型,通常情况下类型的宽度越小越好。如果非要选择字符串类型的话,最好对键值的长度进行额外的约束
Tips: 除了添加键 - 元素对,我们在一个值为nil的字典上做任何操作都不会引起错误。当我们试图在一个值为nil的字典中添加键 - 元素对的时候,Go 语言的运行时系统就会立即抛出一个 panic。