map[K]V
Map 使用前必须初始化 m := make(map[K]V)
。zero value 是 nil
,零值 map 可读不可写,写就 panic。
|
|
Key 类型 K 必须是可比较类型(comparable),即必须可受作用于 ==
和 !=
操作符,slice、map、function 都不行。Value 类型 V 为任意类型 any。
Map[K] 返回结果可以是一个值,也可以是两个值。如果获取一个不存在的 key 对应的值时,会返回零值。为了区分真正的零值和 key 不存在这两种情况,可以根据第二个返回值来区分。
|
|
Map 是无序的。所以当遍历一个 map 对象的时候,无法保证两次遍历元素的顺序是一样的,也不能保证和插入的顺序一致。想要按照 key 的顺序获取 map 的值,需要先取出所有的 key 进行排序,然后按照这个排序的 key 依次获取对应的值。而如果我们想要保证元素有序,比如按照元素插入的顺序进行遍历,可以使用辅助的数据结构,比如 orderedmap,来记录插入顺序。
Map 本身非并发安全,遇并发读写需加锁。加之大多符合读多写少的应用场景,所以一般使用 RWMutex。
用锁去同步多个并发执行体是常用的同步手段。为了保证有锁代码的性能,锁的使用原则就是:尽量减少锁的粒度和锁的持有时间。
我们在业务代码中要尽可能保证临界区内不做耗时操作,这样可以减少锁的持有时间。减少锁的粒度常用方法是分片(shard),把一把锁分成几把锁,每个锁控制一个分片。Go 比较知名的分片并发 map 的实现是 orcaman/concurrent-map
Go 1.9 增加了线程安全的 map 即 sync.Map
,但是它并不是用来替换内建 map 类型的。
官方文档指出,在以下两个场景中使用 sync.Map,会比使用 map + RWMutex 的方式,性能要好得多:
这两个场景说得都比较笼统,而且,这些场景中还包含了一些特殊的情况。所以,官方建议你针对自己的场景做性能评测,如果确实能够显著提高性能,再使用 sync.Map。