【Golang】Go语言Map详解

文章目录

  • 环境
  • 大纲
  • 一、map基础知识
    • map特点
    • map初始化
    • map访问
  • 二、golang实现
    • 1.数据结构
    • 2. 访问
    • 2.扩容(TODO)
  • 总结(TODO)
  • 参考资料


环境

go1.17.8 darwin/arm64

大纲

  • map特点
  • map基本用法
  • hash table 相关知识

一、map基础知识

map特点

  • map是哈希表的引用
  • map需要初始化后使用
  • 由于map是引用类型,所以传递的成本很低,64位机器上占8字节,32位机器上占4字节
  • map的key的类型必须是可使用==和!=比较的
  • map的中存储的元素是无序的

map初始化

  1. 使用字面量,初始化并赋值
m := map[int]string{
	1:"one",
	2:"two",
}
  1. 使用make关键字
m := make(map[int]string)
m[1]="one"
m[2]="two"

map访问

  1. 遍历
m := map[int]string{
	1:"one",
	2:"two",
}

for k,v := range m{
	fmt.Println(k,v)
}
  1. 查找key对应的value
m := map[int]string{
	1:"one",
	2:"two",
}

v1 := m[1]
v2 := m[2]

如果map中对应的key不存在,则返回零值。或者判断key是否存在:

m := map[int]string{
	1:"one",
	2:"two",
}

if v1, ok := m[1]; !ok {
	fmt.Println("key 1 is not exist")
}
  1. 删除key
m := map[int]string{
	1:"one",
	2:"two",
}

delete(m, 1)

二、golang实现

  • go使用拉链法解决hash碰撞问题。
  • 扩容时不会立马迁移和释放原有空间,而是通过后续的访问逐步的将旧空间中的内容迁移到新空间,再由GC释放旧空间。

1.数据结构

【Golang】Go语言Map详解_第1张图片
hmap相当于map指向hash表的头指针:


// A header for a Go map.
type hmap struct {
	// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
	// Make sure this stays in sync with the compiler's definition.
	count     int // # live cells == size of map.  Must be first (used by len() builtin)
	flags     uint8
	B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
	noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
	hash0     uint32 // hash seed

	buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
	oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
	nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

	extra *mapextra // optional fields
}
  • count:map中的现有元素总数
  • flags:缓存hash表的状态变量
  • B:表示bucket的数量,bucket数量=2^B
  • noverflow:计算溢出桶的数量
  • hash0: hash函数的随机数种子,为hash函数添加随机性,map创建时确定
  • buckets: 指向当前桶数组的第一个元素(桶数组的头指针)
  • oldbuckets: 指向旧桶数组的第一个元素
  • extra:指向溢出桶
// A bucket for a Go map.
type bmap struct {
	tophash [bucketCnt]uint8
}

// compile internal struct
type bmap struct {
    tophash  [8]uint8
    keys     [8]keytype
    values   [8]valuetype
    pad      uintptr
    overflow uintptr
}
  • tophash:存储计算出的hash的高8位值

2. 访问

当一个键值对需要插入或者访问时,首先要根据key键找到存储键的桶,在从桶中查找出相应槽位的key和value。这就涉及到如下三个问题:

  • 如何确定是哪个bucket
  • tophash怎么确定
  • 为什么key和value通过地址偏移量访问

golang中通过对key哈希后,取hash(key)的低B位来确定bucket的数组下标。通过hash(key)的高8位来与桶中槽位的tophash相比较从而确定应该存储或访问哪个槽位的数据。由于key和value是集中存储的(所有的key存一块儿,value存一块儿,有利于内存对齐),所以很容易通过地址的偏移量方位内存中相应的数据。图示如下:
【Golang】Go语言Map详解_第2张图片

2.扩容(TODO)


总结(TODO)

参考资料

你可能感兴趣的:(Golang,golang,map,hashtable,hash)