文章目录
Map
在 Go 中 map 是 hash table 的引用,map 类型被写作 map[K]V,K 和 V 分别为键和值的类型。键的类型 K 必须能够使用 == 进行比较,不推荐使用 floats 作为键。
Construct/Destruct
map 可以使用内置的函数 make 创建:
some_map := make(map[string]int) // mapping from strings to ints
也可以直接构造
some_map := map[string] int {
"alice": 31,
"charlie": 34
}
等价于
some_map := make(map[string]int)
some_map["alice"] = 31
some_map["charlie"] = 34
Map key to other types
有时我们需要一个键是 slice 的 map 或者 set,但是不能直接使用 slice 作为 map 的键,因为 map 的键之间是能直接进行比较的。但是我们可以通过以下两步来达到相同的效果:
- 定义 helper 函数 k 将键映射道 string,仅当 x == y 时,k(x) == k(y)。
- 创建一个键为 string 的 map,当访问 map 时,使用 k 函数转换进行访问。
var m = make(map[string]int)
// fmt.Sprintf covert a slice of strings into a single string
func k(list []string) string { return fmt.Sprintf("%q", list) }
func Add(list []string) { m[k(list)]++ }
func Count(list []string) int { return m[k(list)] }
上述方法可以应用于任何不能比较的类型,设置可以借此定义自己的比较函数,键的类型也可以其他可以进行比较的类型。
Element access
使用 [] 进行取值。
some_map["bob"] = some_map["bob"] + 1
即使使用的键不在 map 中,对该键的所有操作也是合法的。使用不存在于 map 中的键进行查找,返回值类型的 0 值。
但是 map 中的元素不是变量,我们不能对其进行取址:
_ := &some_map["bob"] // complie error: cannot take address of map element
不能对 map 元素进行取地址的理由之一是:map 的增长可能导致 rehash 的发生,届时 map 中的元素的存储位置可能发生改变,而导致当前取得的地址非法。
Iterators
使用 range-based 的 for 循环来枚举 map 中的所有键值对:
for key, val := range some_map {
fmt.Printf("%s\t%d\n", key, val )
}
map 迭代器的顺序是不确定的,不同的实现可能使用不同的 hash 函数,导致不同的顺序。
实践中,顺序是随机的,每一次执行都不同。如果希望依序枚举键值对,必须首先对键进行排序,如果键的类型是 string,可以使用 sort package 中的 Strings 函数:
import "sort"
var names []string
for name := range some_map {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
fmt.Printf("%s\t%d\n", name, some_map[name])
}
Capacity
map 的 0 值为 nil,并没有引用一个 hash tabel。
var ages map[string]int
fmt.Println(ages == nil) // "true"
fmt.Println(len(ages) == 0) // "true"
Modifiers
使用 [] 进行插入
some_map["alice"] = 31
some_map["charlie"] = 34
使用内置的函数 delete 移除键值对:
delete(some_map, "alice")
Lookup
判断一个键对应的值是否真实存在于 map 中:
age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
等价于两行语句的集合:
if name, ok := ages["bob"]; !ok {
/* "bob" is not a key in this map; age == 0. */
}
Observers
Compare
和 slice 一样,map 相互之间不能进行比较,唯一合法的比较值是 nil。使用如下方式比较两个 map 是否具有相同的键值对:
func equal(x, y map[string]int) bool {
if len(x) != len() {
return false
}
for k, xv := range x {
if yv, ok = y[k]; ok || yv != xv {
return false
}
}
return true;
}
与 C++ 中的 map 进行比较
map in go | std::map | std::unorder_map | |
---|---|---|---|
实现 | hash table | red-black tree | hash table |
时间复杂度 | 常数时间 | logn时间 | 常数时间 |
键 | 不可重复 | 不可重复 | 不可重复 |
比较方式 | 通过 == 比较 | 通过 Compare = std::less<Key> 比较 | 通过 keyEqual = std::equal_to<Key> |