iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中map扩容底层如何实现
  • 844
分享到

Golang中map扩容底层如何实现

2023-07-05 09:07:57 844人浏览 安东尼
摘要

这篇文章主要讲解了“golang中map扩容底层如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中map扩容底层如何实现”吧!map底层结构主要包含两个核心结构体hmap和

这篇文章主要讲解了“golang中map扩容底层如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中map扩容底层如何实现”吧!

    map底层结构

    主要包含两个核心结构体hmapbmap

    数据会先存储在正常桶hmap.buckets指向的bmap数组中,一个bmap只能存储8组键值对数据,超过则会将数据存储到溢出桶hmap.extra.overflow指向的bmap数组中

    那么,当溢出桶也存储不下了,会怎么办呢,数据得存储到哪去呢?答案,肯定是扩容,那么扩容怎么实现的呢?接着往下看

    Golang中map扩容底层如何实现

    扩容时机

    在向 map 插入新 key 的时候,会进行条件检测,符合下面这 2 个条件,就会触发扩容

    // If we hit the max load factor or we have too many overflow buckets,// and we're not already in the middle of growing, start growing.if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) {  hashGrow(t, h)  goto again // Growing the table invalidates everything, so try again}// growing reports whether h is growing. The growth may be to the same size or bigger.func (h *hmap) growing() bool {  return h.oldbuckets != nil}

    条件1:超过负载

    map元素个数 > 6.5 * 桶个数

    // overLoadFactor reports whether count items placed in 1<<B buckets is over loadFactor.func overLoadFactor(count int, B uint8) bool {  return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)}

    其中

    • bucketCnt = 8,一个桶可以装的最大元素个数

    • loadFactor = 6.5,负载因子,平均每个桶的元素个数

    • bucketShift(B): 桶的个数

    条件2:溢出桶太多

    当桶总数 < 2 ^ 15 时,如果溢出桶总数 >= 桶总数,则认为溢出桶过多。

    当桶总数 >= 2 ^ 15 时,直接与 2 ^ 15 比较,当溢出桶总数 >= 2 ^ 15 时,即认为溢出桶太多了。

    // tooManyOverflowBuckets reports whether noverflow buckets is too many for a map with 1<<B buckets.// Note that most of these overflow buckets must be in sparse use;// if use was dense, then we'd have already triggered regular map growth.func tooManyOverflowBuckets(noverflow uint16, B uint8) bool {  // If the threshold is too low, we do extraneous work.  // If the threshold is too high, maps that grow and shrink can hold on to lots of unused memory.  // "too many" means (approximately) as many overflow buckets as regular buckets.  // See incrnoverflow for more details.  if B > 15 {    B = 15  }  // The compiler doesn't see here that B < 16; mask B to generate shorter shift code.  return noverflow >= uint16(1)<<(B&15)}

    对于条件2,其实算是对条件1的补充。因为在负载因子比较小的情况下,有可能 map 的查找和插入效率也很低,而第 1 点识别不出来这种情况。

    表面现象就是负载因子比较小,即 map 里元素总数少,但是桶数量多(真实分配的桶数量多,包括大量的溢出桶)。比如不断的增删,这样会造成overflow的bucket数量增多,但负载因子又不高,达不到第 1 点的临界值,就不能触发扩容来缓解这种情况。这样会造成桶的使用率不高,值存储得比较稀疏,查找插入效率会变得非常低,因此有了第 2 扩容条件。

    扩容方式

    双倍扩容

    针对条件1,新建一个buckets数组,新的buckets大小是原来的2倍,然后旧buckets数据搬迁到新的buckets,该方法我们称之为双倍扩容

    等量扩容

    针对条件2,并不扩大容量,buckets数量维持不变,重新做一遍类似双倍扩容的搬迁动作,把松散的键值对重新排列一次,使得同一个 bucket 中的 key 排列地更紧密,节省空间,提高 bucket 利用率,进而保证更快的存取,该方法我们称之为等量扩容

    扩容函数

    上面说的 hashGrow() 函数实际上并没有真正地“搬迁”,它只是分配好了新的 buckets,并将老的 buckets 挂到了 oldbuckets 字段上

    真正搬迁 buckets 的动作在 growWork() 函数中,而调用 growWork() 函数的动作是在 mapassign 和 mapdelete 函数中。也就是插入或修改、删除 key 的时候,都会尝试进行搬迁 buckets 的工作。先检查 oldbuckets 是否搬迁完毕,具体来说就是检查 oldbuckets 是否为 nil

    func hashGrow(t *maptype, h *hmap) {  // If we've hit the load factor, get bigger.  // Otherwise, there are too many overflow buckets,  // so keep the same number of buckets and "grow" laterally.  bigger := uint8(1)  if !overLoadFactor(h.count+1, h.B) {    bigger = 0    h.flags |= sameSizeGrow  }  oldbuckets := h.buckets  newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger, nil)  flags := h.flags &^ (iterator | oldIterator)  if h.flags&iterator != 0 {    flags |= oldIterator  }  // commit the grow (atomic wrt GC)  h.B += bigger  h.flags = flags  h.oldbuckets = oldbuckets  h.buckets = newbuckets  h.nevacuate = 0  h.noverflow = 0  if h.extra != nil && h.extra.overflow != nil {    // Promote current overflow buckets to the old generation.    if h.extra.oldoverflow != nil {      throw("oldoverflow is not nil")    }    h.extra.oldoverflow = h.extra.overflow    h.extra.overflow = nil  }  if nextOverflow != nil {    if h.extra == nil {      h.extra = new(mapextra)    }    h.extra.nextOverflow = nextOverflow  }  // the actual copying of the hash table data is done incrementally  // by growWork() and evacuate().}

    由于 map 扩容需要将原有的 key/value 重新搬迁到新的内存地址,如果map存储了数以亿计的key-value,一次性搬迁将会造成比较大的延时,因此 Go map 的扩容采取了一种称为“渐进式”的方式,原有的 key 并不会一次性搬迁完毕,每次最多只会搬迁 2 个 bucket

    func growWork(t *maptype, h *hmap, bucket uintptr) {  // make sure we evacuate the oldbucket corresponding  // to the bucket we're about to use  evacuate(t, h, bucket&h.oldbucketmask())  // evacuate one more oldbucket to make progress on growing  if h.growing() {    evacuate(t, h, h.nevacuate)  }}

    感谢各位的阅读,以上就是“Golang中map扩容底层如何实现”的内容了,经过本文的学习后,相信大家对Golang中map扩容底层如何实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

    您可能感兴趣的文档:

    --结束END--

    本文标题: Golang中map扩容底层如何实现

    本文链接: https://www.lsjlt.com/news/350889.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Golang中map扩容底层如何实现
      这篇文章主要讲解了“Golang中map扩容底层如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中map扩容底层如何实现”吧!map底层结构主要包含两个核心结构体hmap和...
      99+
      2023-07-05
    • 源码剖析Golang中map扩容底层的实现
      目录前言map底层结构扩容时机条件1:超过负载条件2:溢出桶太多扩容方式双倍扩容等量扩容扩容函数总结前言 之前的文章详细介绍过Go切片和map的基本使用,以及切片的扩容机制。本文针对...
      99+
      2023-03-06
      Golang map扩容实现 Golang map扩容 Golang map
    • golang map底层实现原理是什么
      Golang中的map是基于散列表(hash table)实现的。散列表是一种用于存储键值对的数据结构,它通过将键映射到数组的索引来...
      99+
      2023-10-21
      golang
    • Golang中的Slice底层如何实现
      这篇“Golang中的Slice底层如何实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Golang中的Slice底层如何...
      99+
      2023-07-05
    • Go map底层实现、扩容规则和特性分类源码分析
      这篇文章主要介绍“Go map底层实现、扩容规则和特性分类源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go map底层实现、扩容规则和特性分类源码分析”文章能帮助大家解...
      99+
      2023-07-05
    • golang多态底层实现
      Golang多态底层实现Golang是一种静态类型的编程语言,旨在提高编程的生产力和代码的可靠性。其中一个最受欢迎的特性是它的支持多态性,这使得代码可以更加通用、可重用。但是,很少有人探讨Golang多态底层实现的细节。在本文中,我们将讨论...
      99+
      2023-05-14
    • golang如何实现map
      本文小编为大家详细介绍“golang如何实现map”,内容详细,步骤清晰,细节处理妥当,希望这篇“golang如何实现map”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。随着大数据时代的到来和云计算技术的普及,数...
      99+
      2023-07-05
    • golang map如何实现
      本文小编为大家详细介绍“golang map如何实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“golang map如何实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。哈希表的概念哈希表是一种以键值对存储数...
      99+
      2023-07-05
    • 在java中ArrayList集合底层的扩容原理
      目录第一章 前言概述第01节 概述第02节 区别第二章 核心代码第01节 成员变量第02节 构造方法第三章 扩容操作第01节 扩容代码第一章 前言概述 第01节 概述 底层说明 Ar...
      99+
      2024-04-02
    • 深入了解Golang中的Slice底层实现
      目录1 Go数组2 切片的数据结构3 创建切片3.1 方法一:make3.2 方法二:字面量4 nil和空切片5 切片扩容5.1 扩容策略5.2 底层数组是不是新地址range遍历数...
      99+
      2023-02-26
      Golang Slice实现 Golang Slice Go Slice
    • 深入探讨golang的底层实现
      Golang是一种高效、现代化的编程语言,以其快速、简单和安全的开发模式,在近年来越来越受到开发者的欢迎。Go语言不仅支持多线程,还具有良好的并发开发能力,同时它也是一种非常底层的语言,这使得Go语言的底层实现得到了广泛关注。考虑到语言设计...
      99+
      2023-05-14
    • ArrayList和LinkedList的区别、扩容机制以及底层的实现方式
      目录ArrayList和LinkedList区别、扩容机制及底层实现ArrayListLinkedListVestorArrayList的扩容机制LinkedList的扩容机制Arr...
      99+
      2023-05-13
      ArrayList和LinkedList区别 扩容机制 底层实现
    • golang map 删除如何实现
      Golang是一种快速、高效、跨平台的编程语言,作为目前较为流行的编程语言之一,它拥有丰富的特性和各种高级数据结构,比如map。Map是Golang中非常常用的内置数据结构,它可以轻松的在程序中存储键值对类型的数据。Map提供了便捷的操作方...
      99+
      2023-05-14
      Golang
    • 浅谈Golang Slice切片如何扩容的实现
      目录一、Slice数据结构是什么?二、详细代码1.数据结构2.扩容原则3.如何理解扩容规则一1.当小于1024个元素时2.当大于1024个元素时4.如何理解扩容规则二1.简单理解内存...
      99+
      2024-04-02
    • Golang函数底层实现原理探讨
      Golang函数底层实现原理探讨Golang语言中的函数是非常重要的一个特性,但是很少有人关注其底层实现原理。本文将深入探讨Golang函数的底层实现原理,希望读者能够更好地理解和优化自己的代码。Golang函数的定义在Golang中,函数...
      99+
      2023-05-17
      函数 Golang 底层实现
    • Golang中map的声明定义如何实现
      本篇内容主要讲解“Golang中map的声明定义如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中map的声明定义如何实现”吧!定义map,在golang中定义为map[str...
      99+
      2023-07-05
    • css如何实现弹出层覆盖底层效果
      这篇文章主要介绍“css如何实现弹出层覆盖底层效果”,在日常操作中,相信很多人在css如何实现弹出层覆盖底层效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”css如何实现弹出层覆盖底层效果”的疑惑有所帮助!...
      99+
      2023-07-04
    • ArrayList和LinkedList的区别、扩容机制及底层的实现方式是什么
      这篇文章主要介绍“ArrayList和LinkedList的区别、扩容机制及底层的实现方式是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ArrayList和LinkedList的区别、扩容机制...
      99+
      2023-07-05
    • 详解Golang中NewTimer计时器的底层实现原理
      目录1.简介2.基本使用3.实现原理3.1 内容分析3.2 基本思路3.3 实现步骤3.4 NewTimer的实现4.总结1.简介 本文将介绍 Go 语言中的NewTimer,首先展...
      99+
      2023-05-18
      Golang NewTimer计时器原理 Golang NewTimer计时器 Golang 计时器
    • spring aop底层原理及如何实现
      目录前言 使用 源码分析 总结 前言 相信每天工作都要用spring框架的大家一定使用过spring aop,aop的概念是面向切面编程,相对与传统的面向对象编程oop,aop更关...
      99+
      2024-04-02
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作