返回

Go map 源码逐行拆解

后端

Go map 是 Go 语言中一种重要的数据结构,用于存储键值对。它是一种无序集合,这意味着元素没有特定的顺序。map 非常高效,因为它使用哈希表来存储键,从而实现 O(1) 的查找时间复杂度。

让我们逐行拆解 Go map 的核心源码,深入了解其底层实现原理。

type hmap struct {
    count     int          // # live cells == size of map.  Must be first (ie. before buckets).
    flags     uint8        // map flags.
    B         uint8        // number of buckets (B == len(buckets)).
    noverflow uint16       // number of overflow buckets; or 0.
    buckets    []*bmap      // array of buckets.
    oldbuckets []*bmap      // previous bucket array (during map expansion).
    nelems     int          // count of elements in map (for iterating).
    hash0      uint32       // hash seed.
}

hmap 是 Go map 的底层实现。它包含以下段:

  • count: 活跃元素的数量,即 map 的大小。
  • flags: map 标志,用于指示 map 的状态和特性。
  • B: 桶的数量。
  • noverflow: 溢出桶的数量(如果存在)。
  • buckets: 存储桶的数组。
  • oldbuckets: 在 map 扩容期间使用的前一个桶数组。
  • nelems: map 中元素的数量(用于迭代)。
  • hash0: 哈希种子,用于哈希函数。
type bmap struct {
    tophash   uint8  // top-level bucket hash.
    overflow  *bmap  // overflow bucket (if not last).
    nelems    int
    data       [128]entry
}

bmap 是 map 中的桶。它包含以下关键字段:

  • tophash: 顶级桶哈希值。
  • overflow: 溢出桶(如果不是最后一个)。
  • nelems: 桶中元素的数量。
  • data: 存储键值对的数组。
type entry struct {
    key       *Key
    val       *Value
    keysize    int
    valsize    int
    hash       uint32
    next       *entry
}

entry 是 map 中的键值对条目。它包含以下关键字段:

  • key: 键。
  • val: 值。
  • keysize: 键的大小。
  • valsize: 值的大小。
  • hash: 键的哈希值。
  • next: 指向下一个条目的指针(如果存在溢出)。

查找操作

查找操作通过哈希函数计算键的哈希值,然后使用哈希值作为索引查找相应的桶。如果桶中存在匹配的键,则返回相应的值。如果桶中不存在匹配的键,则搜索溢出桶(如果有)。

插入操作

插入操作与查找操作类似。如果桶中存在匹配的键,则更新相应的值。如果桶中不存在匹配的键,则将新的键值对添加到桶中。如果桶已满,则创建一个溢出桶并将其添加到桶中。

删除操作

删除操作通过哈希函数计算键的哈希值,然后使用哈希值作为索引查找相应的桶。如果桶中存在匹配的键,则删除相应的键值对。如果桶中不存在匹配的键,则搜索溢出桶(如果有)。

并发安全

Go map 使用读写锁来实现并发安全。当对 map 进行写入操作时,会获取写锁。当对 map 进行读取操作时,会获取读锁。这样可以确保对 map 的并发访问是安全的。

结论

通过逐行拆解 Go map 的核心源码,我们深入了解了其底层实现原理。Go map 使用哈希表和读写锁来提供高效、并发安全的键值对存储。掌握 Go map 的核心知识可以帮助我们编写更高效、更健壮的 Go 程序。