范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Go语言复合类型map的基本操作

  插入新键值对
  面对一个非 nil 的 map 类型变量,我们可以在其中插入符合 map 类型定义的任意新键值对。插入新键值对的方式很简单,我们只需要把 value 赋值给 map 中对应的 key 就可以了:  m := make(map[int]string) m[1] = "value1" m[2] = "value2" m[3] = "value3"
  Go 运行时会负责 map 变量内部的内存管理,因此除非是系统内存耗尽,我们可以不用担心向 map 中插入新数据的数量和执行结果。
  如果我们插入新键值对的时候,某个 key 已经存在于 map 中了,那我们的插入操作就会用新值覆盖旧值:  m := map[string]int {   "key1" : 1,   "key2" : 2, }  m["key1"] = 11 // 11会覆盖掉"key1"对应的旧值1 m["key3"] = 3  // 此时m为map[key1:11 key2:2 key3:3]
  获取键值对数量
  想知道当前 map 类型变量中已经建立了多少个键值对,那我们可以怎么做呢?和切片一样,map 类型也可以通过内置函数 len,获取当前变量已经存储的键值对数量:  m := map[string]int {   "key1" : 1,   "key2" : 2, }  fmt.Println(len(m)) // 2 m["key3"] = 3   fmt.Println(len(m)) // 3
  查找和数据读取
  和写入相比,map 类型更多用在查找和数据读取场合。  m := make(map[string]int) v := m["key1"]
  第二行代码在语法上好像并没有什么不当之处,但其实通过这行语句,我们还是无法确定键 key1 是否真实存在于 map 中。这是因为,当我们尝试去获取一个键对应的值的时候,如果这个键在 map 中并不存在,我们也会得到一个值,这个值是 value 元素类型的零值。
  Go 语言的 map 类型支持通过用一种名为"comma ok"的惯用法,进行对某个 key 的查询。接下来我们就用"comma ok"惯用法改造一下上面的代码:  m := make(map[string]int) v, ok := m["key1"] if !ok {     // "key1"不在map中 }  // "key1"在map中,v将被赋予"key1"键对应的value
  这里我们通过了一个布尔类型变量 ok,来判断键"key1"是否存在于 map 中。如果存在,变量 v 就会被正确地赋值为键"key1"对应的 value。一定要记住:在 Go 语言中,请使用"comma ok"惯用法对 map 进行键查找和键值读取操作。
  删除数据
  在 Go 中,我们需要借助内置函数 delete 来从 map 中删除数据。使用 delete 函数的情况下,传入的第一个参数是我们的 map 类型变量,第二个参数就是我们想要删除的键。  m := map[string]int {   "key1" : 1,   "key2" : 2, }  fmt.Println(m) // map[key1:1 key2:2] delete(m, "key2") // 删除"key2" fmt.Println(m) // map[key1:1]
  delete 函数是从 map 中删除键的唯一方法。即便传给 delete 的键在 map 中并不存在,delete 函数的执行也不会失败,更不会抛出运行时的异常。
  遍历 map 中的键值数据
  在 Go 中,遍历 map 的键值对只有一种方法,那就是像对待切片那样通过 for range 语句对 map 数据进行遍历。  package main    import "fmt"  func main() {     m := map[int]int{         1: 11,         2: 12,         3: 13,     }      fmt.Printf("{ ")     for k, v := range m {         fmt.Printf("[%d, %d] ", k, v)     }     fmt.Printf("} ") }
  通过 for range 遍历 map 变量 m,每次迭代都会返回一个键值对,其中键存在于变量 k 中,它对应的值存储在变量 v 中。
  对同一 map 做多次遍历的时候,每次遍历元素的次序都不相同。这是 Go 语言 map 类型的一个重要特点,也是很容易让 Go 初学者掉入坑中的一个地方。所以这里你一定要记住:程序逻辑千万不要依赖遍历 map 所得到的的元素次序。
  map 的内部实现
  Go 运行时使用一张哈希表来实现抽象的 map 类型。运行时实现了 map 类型操作的所有功能,包括查找、插入、删除等。在编译阶段,Go 编译器会将 Go 语法层面的 map 操作,重写成运行时对应的函数调用。大致的对应关系是这样的:  // 创建map类型变量实例 m := make(map[keyType]valType, capacityhint) → m := runtime.makemap(maptype, capacityhint, m)  // 插入新键值对或给键重新赋值 m["key"] = "value" → v := runtime.mapassign(maptype, m, "key") v是用于后续存储value的空间的地址  // 获取某键的值  v := m["key"]      → v := runtime.mapaccess1(maptype, m, "key") v, ok := m["key"]  → v, ok := runtime.mapaccess2(maptype, m, "key")  // 删除某键 delete(m, "key")   → runtime.mapdelete(maptype, m, "key")
  这是 map 类型在 Go 运行时层实现的示意图:
  和切片的运行时表示图相比,map 的实现示意图显然要复杂得多,我们重点关注一个 map 变量在初始状态、进行键值对操作后,以及在并发场景下的 Go 运行时层的实现原理。
  1、初始状态
  从图中我们可以看到,与语法层面 map 类型变量(m)一一对应的是 *runtime.hmap 的实例,即 runtime.hmap 类型的指针,也就是我们前面在讲解 map 类型变量传递开销时提到的 map 类型的描述符。hmap 类型是 map 类型的头部结构(header),它存储了后续 map 类型操作所需的所有信息,包括:
  真正用来存储键值对数据的是桶,也就是 bucket,每个 bucket 中存储的是 Hash 值低 bit 位数值相同的元素,默认的元素个数为 BUCKETSIZE(值为 8,Go 1.17 版本中在 $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go 中定义,与 runtime/map.go 中常量 bucketCnt 保持一致)。当某个 bucket(比如 buckets[0]) 的 8 个空槽 slot)都填满了,且 map 尚未达到扩容的条件的情况下,运行时会建立 overflow bucket,并将这个 overflow bucket 挂在上面 bucket(如 buckets[0])末尾的 overflow 指针上,这样两个 buckets 形成了一个链表结构,直到下一次 map 扩容之前,这个结构都会一直存在。
  从图中我们可以看到,每个 bucket 由三部分组成,从上到下分别是 tophash 区域、key 存储区域和 value 存储区域。
  tophash 区域:
  当我们向 map 插入一条数据,或者是从 map 按 key 查询数据的时候,运行时都会使用哈希函数对 key 做哈希运算,并获得一个哈希值(hashcode)。这个 hashcode 非常关键,运行时会把 hashcode"一分为二"来看待,其中低位区的值用于选定 bucket,高位区的值用于在某个 bucket 中确定 key 的位置。如下图:
  每个 bucket 的 tophash 区域其实是用来快速定位 key 位置的,这样就避免了逐个 key 进行比较这种代价较大的操作。
  key 存储区域:
  tophash 区域下面是一块连续的内存区域,存储的是这个 bucket 承载的所有 key 数据。运行时在分配 bucket 的时候需要知道 key 的 Size。那么运行时是如何知道 key 的 size 的呢?当我们声明一个 map 类型变量,比如 var m map[string]int 时,Go 运行时就会为这个变量对应的特定 map 类型,生成一个 runtime.maptype 实例。如果这个实例已经存在,就会直接复用。maptype 实例的结构是这样的:  type maptype struct {     typ        _type     key        *_type     elem       *_type     bucket     *_type // internal type representing a hash bucket     keysize    uint8  // size of key slot     elemsize   uint8  // size of elem slot     bucketsize uint16 // size of bucket     flags      uint32 }
  这个实例包含了我们需要的 map 类型中的所有"元信息"。我们前面提到过,编译器会把语法层面的 map 操作重写成运行时对应的函数调用,这些运行时函数都有一个共同的特点,那就是第一个参数都是 maptype 指针类型的参数。Go 运行时就是利用 maptype 参数中的信息确定 key 的类型和大小的。map 所用的 hash 函数也存放在 maptype.key.alg.hash(key, hmap.hash0) 中。同时 maptype 的存在也让 Go 中所有 map 类型都共享一套运行时 map 操作函数,而不是像 C++ 那样为每种 map 类型创建一套 map 操作函数,这样就节省了对最终二进制文件空间的占用。
  value 存储区域:
  这个区域存储的是 key 对应的 value。和 key 一样,这个区域的创建也是得到了 maptype 中信息的帮助。Go 运行时采用了把 key 和 value 分开存储的方式,而不是采用一个 kv 接着一个 kv 的 kv 紧邻方式存储,这带来的其实是算法上的复杂性,但却减少了因内存对齐带来的内存浪费。
  我们以 map[int8]int64 为例,看看下面的存储空间利用率对比图:
  当前 Go 运行时使用的方案内存利用效率很高,而 kv 紧邻存储的方案在 map[int8]int64 这样的例子中内存浪费十分严重,它的内存利用率是 72/128=56.25%,有近一半的空间都浪费掉了。
  如果 key 或 value 的数据长度大于一定数值,那么运行时不会在 bucket 中直接存储数据,而是会存储 key 或 value 数据的指针。目前 Go 运行时定义的最大 key 和 value 的长度是这样的:  // $GOROOT/src/runtime/map.go const (     maxKeySize  = 128     maxElemSize = 128 )
  map 扩容
  map 会对底层使用的内存进行自动管理。因此,在使用过程中,当插入元素个数超出一定数值后,map 一定会存在自动扩容的问题,也就是怎么扩充 bucket 的数量,并重新在 bucket 间均衡分配数据的问题。
  Go 运行时的 map 实现中引入了一个 LoadFactor(负载因子),当 count > LoadFactor * 2^B 或 overflow bucket 过多时,运行时会自动对 map 进行扩容。
  这两方面原因导致的扩容,在运行时的操作其实是不一样的。如果是因为 overflow bucket 过多导致的"扩容",实际上运行时会新建一个和现有规模一样的 bucket 数组,然后在 assign 和 delete 时做排空和迁移。
  map 与并发
  从上面的实现原理来看,充当 map 描述符角色的 hmap 实例自身是有状态的(hmap.flags),而且对状态的读写是没有并发保护的。所以说 map 实例不是并发写安全的,也不支持并发读写。如果我们对 map 实例进行并发读写,程序运行时就会抛出异常。你可以看看下面这个并发读写 map 的例子:  package main  import (     "fmt"     "time" )  func doIteration(m map[int]int) {     for k, v := range m {         _ = fmt.Sprintf("[%d, %d] ", k, v)     } }  func doWrite(m map[int]int) {     for k, v := range m {         m[k] = v + 1     } }  func main() {     m := map[int]int{         1: 11,         2: 12,         3: 13,     }      go func() {         for i := 0; i < 1000; i++ {             doIteration(m)         }     }()      go func() {         for i := 0; i < 1000; i++ {             doWrite(m)         }     }()      time.Sleep(5 * time.Second) }
  我们仅仅是进行并发读,map 是没有问题的。而且,Go 1.9 版本中引入了支持并发写安全的 sync.Map 类型,可以在并发读写的场景下替换掉 map。
  考虑到 map 可以自动扩容,map 中数据元素的 value 位置可能在这一过程中发生变化,所以 Go 不允许获取 map 中 value 的地址,这个约束是在编译期间就生效的。

1。31赛事分析德国杯意大利杯国际米兰vs亚特兰大内附比分视频加载中003德国杯柏林联合vs沃尔夫斯堡柏林联合最近的状态是非常好,球队的攻防一体做得是非常好,火力进攻是非常的强势,场均进2球,反观在防守方面柏林联合做得也是非常出色,多场比遇见梅好!快来上海动物园赏梅吧在上海动物园通往出口的道路上,大片蜡梅正传来阵阵馨香。今年的蜡梅于1月初开放,于近日迎来了最佳观赏期。若是阳光灿烂的冬日,朵朵小黄花在蓝天的映衬下,犹如生命力最为蓬勃的梵高的画作逆春节假期,国际旅游度假区累计接待游客63。35万人次上海迪士尼乐园人气爆棚比斯特上海购物村持续拉动假期消费经济热度温泉住宿房车等同样颇受欢迎记者从上海国际旅游度假区管委会获悉,今年春节假期,上海国际旅游度假区累计接待游客63。35万越冷越火热黑龙江深挖优势续写冰雪经济发展新篇章来源人民网北国风光,千里冰封,万里雪飘。在我国最东端和最北端的黑龙江省,进入了一年中气候最冷旅游热度最高的季节。巨型露天雪人冰灯里找鱼万人雪地大蹦迪龙江大地上,丰富多彩的活动和一个荡口古镇河南卫视,强强联合的元宵文化盛宴来啦!元YUANXIAO宵俗话说正月里都是年过完了春节就是元宵啦正月是农历的元月古人称夜为宵正月十五日是一年中第一个月圆之夜所以称正月十五为元宵节元宵节怎么少得了元宵奇妙游呢?由河南卫视大赛车博物馆春节参观人次增2。3倍业界门票合作计划2月1日接受申请澳门大赛车博物馆于春节黄金周共有4,289人次入场参观,较去年春节黄金周增2。3倍。春节假期入场旅客约占8成在春节黄金周(1月21日至1月27日)澳门大赛车博物馆录得4,289参观云海氤氲,玉树琼花,云南江川现雾凇奇观受强冷空气影响,这两天,云南省玉溪市江川区谷堆山迎来今年首场雾凇景观,玉树琼花间云海氤氲涌动,为新春增添了一份别样年景。登上谷堆山顶,放眼望去,只见若隐若现的浓雾里,整座山峦像穿上人民建议打开资源枯竭型城市的文旅富矿来源人民网辽宁频道原创稿我们阜新虽然是全国第一个资源枯竭型城市,但是我发现我们还是有很多文化底蕴的,玉龙故乡海棠山佛寺村等等这是前不久,一位在外地工作多年的网友通过人民网领导留言板广西南宁市青秀区值得游玩的旅游景点有哪些?自驾游有什么攻略?南宁头条青秀区,隶属于广西壮族自治区南宁市,是南宁市的核心城区,青秀区位于广西首府南宁市东部,与横州市接壤,毗邻兴宁区邕宁区,与良庆区江南区隔江相望。青秀区地处北回归线以南,境内以三清山传统文化氛围浓旅游市场人气旺来源江西日报江西新闻客户端江西新闻客户端讯(江西日报全媒体记者余燕通讯员李佳)春节假期,三清山风景名胜区内人潮涌动,游客们在欣赏秀美自然风光的同时,也深切感受到了景区浓浓的文化味。文化旅游雪映丹霞别样美来源新甘肃雪映丹霞别样美雪后的平山湖大峡谷吸引游人前来色彩绚丽的峡谷舒眉雪落无声。甚至,无形。似有若无,只感觉润湿扑面,沁人心脾,定睛凝神,却无踪影,如雾,只在远处氤氲。似霞,早已
最新通知!这笔费用可缓缴近日,四川省人力资源和社会保障厅等4部门发布关于进一步做好阶段性缓缴社会保险费政策实施工作有关问题的通知,通知进一步扩大了阶段性缓缴社会保险费实施范围,我省受疫情影响较大生产经营困向日葵智能插线板,解锁了在家控制单位电脑的技能每天下班回到家最担心的不是需要辅导孩子的功课,而是领导打来电话要求抓紧时间完成任务,很多文件与资料都是保存在单位的电脑里,一般情况下都会是拖着疲惫的身体驱车回单位,把需要完成的工作以两山理念为指引推动旅游业绿色发展党的二十大是一次具有里程碑意义的重要会议。习近平总书记代表第十九届中央委员会向大会所作报告提出,必须牢固树立和践行绿水青山就是金山银山的理念,站在人与自然和谐共生的高度谋划发展。旅堪培拉机场全新礼品店为旅客展示首都的特色商品堪培拉机场全新礼品店为旅客展示首都的特色商品当我们在一个陌生的城市着陆,一定很想看看这里都有什么。不过最好是在离开机场之前去一探究竟,因为万一发现这里的东西自己并不喜欢,还可以搭乘王吾平西夏的风西夏的风西夏的风轻抚着银川这座历史悠久地塞上古城,它是国家历史文化名城,民间传说中又称凤凰城,古称兴庆府宁夏城,素有塞上江南鱼米之乡的美誉,有许多著名的国家级风景区。踏着秋冬的节奏叫你知道我们江南七怪的厉害据说老式电视机里边的雪花来自于138亿年前的宇宙大爆炸,这种宇宙微波背景辐射曾经叫两个年轻人1978年稀里糊涂地获得了诺贝尔物理学奖。这让我在当初的深恶痛绝之外,对那堆刺啦刺啦吵闹品牌realme双11降价信息汇总你有换机打算吗?今晚20点,京东会率先开启双十一的预售,而各大手机厂商大部分也会开启促销,有网友整理了realme旗下所有产品的降价信息。如上图所见,降价力度最大的是真我GT2Pro,真我Q5系列国产放大招?海信U8H凭借自研芯片打败进口,重新定义高端电视以往提到高端智能电视,大多数人第一时间想起三星索尼等进口产品,似乎国产品牌根本拿不出手。可现在情况大为改观,海信新品U8H凭借自研芯片,掌握了高端电视的核心技术,不仅成功打败海外竞不拖了,摊牌了iPadOS16发布在即,迎来大更新自从苹果推迟发布iPadOS16以来,其发布日期一直悬而未决。但除了今天发布2022iPadPro之外,Apple最终确认平板电脑操作系统更新将于10月24日星期一推出。下周推出的国内想要使用TikTok?这些你必须得掌握许多人做外贸的小伙伴在使用TikTok时,老是网络问题打不开,那是因为TikTok对国内用户进行封禁了。那么如果你想要使用TikTok是不是就没有办法了?当然不是,虽然TikTok从XR升级到14ProMax是什么体验?1。续航首先,电池数据方面。从XR的2942毫安提升到14PM的4323毫安有了近百分之50的提升。官方视频播放时长从16小时提升至29小时,将近一倍的时间。通过近一个月的真实使用