map
是Go语言中基础的数据结构,在日常的使用中经常被用到。但是它底层是如何实现的呢?
Golang中map
的底层实现是一个散列表,因此实现map
的过程实际上就是实现散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫hmap
(a header for a go map
),一个叫bmap
(a bucket for a Go map
,通常叫其bucket
)。这两种结构的样子分别如下所示:
hmap:
图中有很多字段,但是便于理解map
的架构,你只需要关心的只有一个,就是标红的字段:buckets数组。Golang的map中用于存储的结构是bucket数组。而bucket(即bmap
)的结构是怎样的呢?
bucket:
相比于hmap
,bucket的结构显得简单一些,标红的字段依然是“核心”,我们使用的map
中的key和value就存储在这里。“高位哈希值”数组记录的是当前bucket中key相关的“索引”,稍后会详细叙述。还有一个字段是一个指向扩容后的bucket的指针,使得bucket会形成一个链表结构。例如下图:
由此看出hmap
和bucket
的关系是这样的:
而bucket又是一个链表,所以,整体的结构应该是这样的:
哈希表的特点是会有一个哈希函数,对你传来的key进行哈希运算,得到唯一的值,一般情况下都是一个数值。Golang的map
中也有这么一个哈希函数,也会算出唯一的值,对于这个值的使用,Golang也是很有意思。
Golang把求得的值按照用途一分为二:高位和低位。
如图所示,蓝色为高位,红色为低位。
然后低位用于寻找当前key属于hmap
中的哪个bucket,而高位用于寻找bucket中的哪个key。上文中提到:bucket中有个属性字段是“高位哈希值”数组,这里存的就是蓝色的高位值,用来声明当前bucket中有哪些“key”,便于搜索查找。
需要特别指出的一点是:我们map
中的key/value值都是存到同一个数组中的。数组中的顺序是这样的:
并不是key0/value0/key1/value1的形式,这样做的好处是:在key和value的长度不同的时候,可以消除padding带来的空间浪费。
现在,我们可以得到Go语言map
的整个的结构图了:
有疑问加站长微信联系(非本文作者))
