Map in Go

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 的键之间是能直接进行比较的。但是我们可以通过以下两步来达到相同的效果:

  1. 定义 helper 函数 k 将键映射道 string,仅当 x == y 时,k(x) == k(y)。
  2. 创建一个键为 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 gostd::mapstd::unorder_map
实现hash tablered-black treehash table
时间复杂度常数时间logn时间常数时间
不可重复不可重复不可重复
比较方式通过 == 比较通过 Compare = std::less<Key> 比较通过 keyEqual = std::equal_to<Key>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值