关于Go的Map的一些小介绍

226 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

3. map

推荐这篇博文 Map底层实现

3.1 底层结构

type hmap struct {
	// Make sure this stays in sync with the compiler's definition.
	count     int // 键值对的数量
	flags     uint8 
	// 状态标识,比如正在被写、buckets和oldbuckets在被遍历、等量扩容(Map扩容相关字段)
	B         uint8  // 2^B=len(buckets)
	noverflow uint16 // 溢出桶里bmap大致的数量
	hash0     uint32 // hash 因子
	buckets    unsafe.Pointer 
	// 指向一个数组(连续内存空间),数组的类型为[]bmap,这个字段我们可以称之为正常桶
	oldbuckets unsafe.Pointer 
	// 扩容时,存放之前的buckets(Map扩容相关字段)
	nevacuate  uintptr        
	// 分流次数,成倍扩容分流操作计数的字段(Map扩容相关字段)
	extra *mapextra 
	// 溢出桶结构,正常桶里面某个bmap存满了,会使用这里面的内存空间存放键值对
}

map的读是无序的,如果需要顺序读取,可以将这个key进行排序,然后再按照这个key打印输出

3.2 赋值

var mp1 map[int][int] // 1
mp1[1]=1
mp2 := map[int][int]{} // 2
mp2[1]=1

情况 1 :报错,var的赋值 mp1是为nil的。 情况 2 :可行,而第二种情况的赋值是不为nil的。

3.3 map 是线程安全的吗?

map的底层hmap是没有加锁的。所以并不是线程安全的。如果需要线程安全的map,我们可以使用官方定义的sync下的sync.Map。

3.4 map的扩容注意点

我们可以看出正常桶的bmap和溢出桶的bmap实际构成了链表关系,所以Go里面Map的实现主要用到了数组,其次还用到了链表。

以下写法是错误的,因为当map扩容的时候,他的扩容地址会发生变化,所以key value都是不可取地址。并且map删除key之后是不会自动缩容的。

m:=make(map[int][int])
m[1]=1
fmt.Println(&m[1])