首页 > 编程知识 正文

深度剖析go map源码

时间:2023-11-21 20:11:15 阅读:294076 作者:PVWJ

本文将从多个方面对go map源码进行详细的阐述,包括map的原理、构造方法、基本操作、扩容机制等,让读者对map有更深入的理解。

一、map的原理

在go中,map是一种无序的键值对集合。其底层实现是哈希表。基于哈希表的实现能够快速地根据键访问值,在提供近乎常数时间的复杂度的同时,保证添加、删除、查找、改变某个键值等操作的效率。

哈希表的基本思想就是将键映射到数组索引的计算过程,这个计算过程称为哈希函数。具体的实现是:先根据键算出哈希值,然后使用哈希值对数组的长度取模得到一个索引值,最后将该键值对插到该索引处。

然而,在实际使用中,哈希表会存在哈希冲突的问题,即不同键映射到同一个索引处。此时,可以将键值对插入数组索引处的位置表示成一个链表/树,称之为桶(bucket)。Go的map底层使用的是散列表(哈希表)和链表(桶)混合实现,初始的桶可称之为BUCKET数组 (map结构体所包含的hmap结构体中), 为了尽量避免哈希冲突,map内部对BUCKET数组循环使用……

二、map的构造方法

go标准库的map构造方法如下:

func make(map[int]int, 100) // 创建一个可存100个键值对的map,键和值都是int类型。

make(map[k键类型]v值类型, initSize初始长度或者分配的内存大小)

关于内存大小,make函数通过内置函数new申请一个指定类型的对象,并返回指向该对象的指针。 我们可以使用GODEBUG="gctrace=1"环境测试内存自动管理gc。

三、map的基本操作

Go标准库提供了map的以下基本操作:

1. 判断key是否在map中

_, ok := m[key]

通过_, ok := m[key],我们不仅可以判断一个值是否存在,而且可以直接访问到值,完成操作。

2. 获取值

value, ok := m[key]

如果key存在于map中,value将是相应的值且ok为true;否则value将是该值类型的零值且ok为false。

3. 修改元素

m[key] = value

如果key存在于map中,它的值将被value替换。否则,它将插入一个新元素。

4. 删除元素

delete(m, key)

从map中删除元素key。如果key在map中不存在,则不执行任何操作。

四、map的扩容机制

当map中元素个数大于hmap结构体中的B指定pts,即桶数量时,会触发map扩容,也就是重新分配BUCKET数组,具体操作是:

1. 申请新BUCKET数组

oldbuckets := hmap.buckets
newbuckets := make([]*bmap, nextBucketCnt, ptrSize )//  ptrSize是指针长度,下一个扩容桶的长度为:BUCKET存储的元素的个数*2
以上nextBucketCnt计算方式为:
if h.sameSizeGrow()< 0 {  // sameSizeGrow是判断是否连续扩容,连续扩容的情况下桶个数每次翻倍增长!
	nextBucketCnt = uintptr(h.BUCKETCOUNT<<1) + 1}
else {
	nextBucketCnt = h.BUCKETCOUNT + uintptr(notSameSizeGrowStep*h.BUCKETCOUNT>>Q)}

2. 寻找新的hash值

遍历BUCKET数组,将元素放入新的BUCKET数组中。

3. 将老的BUCKET数组中的元素对象清空

for i := 0; i < len(oldbuckets); i++ {
	oldbucket := oldbuckets[i]
	if oldbucket != nil {
		for j := 0; j < len(oldbucket)); j++ {
			e := &oldbucket[j]
			if e.key != nil {
				// 清空
				e.key = nil
				e.value = nil
			}
		}
	}
} 

至此,map扩容操作就完成了。需要注意的是,go语言中map的元素类型只要内存地址不变,插入元素的键和值为非指针类型的做write操作将可能导致:值由于出现哈希冲突被重新分配到新的桶中了。

五、总结

本文主要对go map源码进行了分析,包括map的原理、构造方法、基本操作、扩容机制等。通过对map的深入剖析,更好地了解了go语言的map数据结构,希望能够对读者和开发者有所帮助。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。