返回顶部
首页 > 资讯 > 精选 >如何正确使用Go Map
  • 385
分享到

如何正确使用Go Map

2023-06-15 13:06:27 385人浏览 八月长安
摘要

本篇内容主要讲解“如何正确使用Go Map”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何正确使用Go Map”吧!前言例子如下:func main() { &n

本篇内容主要讲解“如何正确使用Go Map”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何正确使用Go Map”吧!

前言

例子如下:

func main() {  m := make(map[int32]string)  m[0] = "EDDYCJY1"  m[1] = "EDDYCJY2"  m[2] = "EDDYCJY3"  m[3] = "EDDYCJY4"  m[4] = "EDDYCJY5"   for k, v := range m {   log.Printf("k: %v, v: %v", k, v)  } }

假设运行这段代码,输出的结果是怎么样?是有序,还是无序输出呢?

k: 3, v: EDDYCJY4 k: 4, v: EDDYCJY5 k: 0, v: EDDYCJY1 k: 1, v: EDDYCJY2 k: 2, v: EDDYCJY3

从输出结果上来讲,是非固定顺序输出的,也就是每次都不一样。但这是为什么呢?

首先建议你先自己想想原因。其次我在面试时听过一些说法。有人说因为是哈希的所以就是无(乱)序等等说法。当时我是有点 ???

这也是这篇文章出现的原因,希望大家可以一起研讨一下,理清这个问题 :)

看一下汇编

   ... 0x009b 00155 (main.go:11) LEAQ type.map[int32]string(SB), AX 0x00a2 00162 (main.go:11) PCDATA $2, $0 0x00a2 00162 (main.go:11) MOVQ AX, (SP) 0x00a6 00166 (main.go:11) PCDATA $2, $2 0x00a6 00166 (main.go:11) LEAQ ""..autotmp_3+24(SP), AX 0x00ab 00171 (main.go:11) PCDATA $2, $0 0x00ab 00171 (main.go:11) MOVQ AX, 8(SP) 0x00b0 00176 (main.go:11) PCDATA $2, $2 0x00b0 00176 (main.go:11) LEAQ ""..autotmp_2+72(SP), AX 0x00b5 00181 (main.go:11) PCDATA $2, $0 0x00b5 00181 (main.go:11) MOVQ AX, 16(SP) 0x00ba 00186 (main.go:11) CALL runtime.mapiterinit(SB) 0x00bf 00191 (main.go:11) JMP 207 0x00c1 00193 (main.go:11) PCDATA $2, $2 0x00c1 00193 (main.go:11) LEAQ ""..autotmp_2+72(SP), AX 0x00c6 00198 (main.go:11) PCDATA $2, $0 0x00c6 00198 (main.go:11) MOVQ AX, (SP) 0x00ca 00202 (main.go:11) CALL runtime.mapiternext(SB) 0x00cf 00207 (main.go:11) CMPQ ""..autotmp_2+72(SP), $0 0x00d5 00213 (main.go:11) JNE 193 ...

我们大致看一下整体过程,重点处理 Go map 循环迭代的是两个 runtime 方法,如下:

  • runtime.mapiterinit

  • runtime.mapiternext

但你可能会想,明明用的是 for range 进行循环迭代,怎么出现了这两个函数,怎么回事?

看一下转换后

var hiter map_iteration_struct for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {     index_temp = *hiter.key     value_temp = *hiter.val     index = index_temp     value = value_temp     original body }

实际上编译器对于 slice 和 map 的循环迭代有不同的实现方式,并不是 for 一扔就完事了,还做了一些附加动作进行处理。而上述代码就是 for  range map 在编译器展开后的伪实现

看一下源码

runtime.mapiterinit

func mapiterinit(t *maptype, h *hmap, it *hiter) {  ...  it.t = t  it.h = h  it.B = h.B  it.buckets = h.buckets  if t.bucket.kind&kindNoPointers != 0 {   h.createOverflow()   it.overflow = h.extra.overflow   it.oldoverflow = h.extra.oldoverflow  }   r := uintptr(fastrand())  if h.B > 31-bucketCntBits {   r += uintptr(fastrand()) << 31  }  it.startBucket = r & bucketMask(h.B)  it.offset = uint8(r >> h.B & (bucketCnt - 1))  it.bucket = it.startBucket     ...   mapiternext(it) }

通过对 mapiterinit 方法阅读,可得知其主要用途是在 map  进行遍历迭代时进行初始化动作。共有三个形参,用于读取当前哈希表的类型信息、当前哈希表的存储信息和当前遍历迭代的数据

为什么

咱们关注到源码中 fastrand 的部分,这个方法名,是不是迷之眼熟。没错,它是一个生成随机数的方法。再看看上下文:

... // decide where to start r := uintptr(fastrand()) if h.B > 31-bucketCntBits {  r += uintptr(fastrand()) << 31 } it.startBucket = r & bucketMask(h.B) it.offset = uint8(r >> h.B & (bucketCnt - 1))  // iterator state it.bucket = it.startBucket

在这段代码中,它生成了随机数。用于决定从哪里开始循环迭代。更具体的话就是根据随机数,选择一个桶位置作为起始点进行遍历迭代

因此每次重新 for range map,你见到的结果都是不一样的。那是因为它的起始位置根本就不固定!

runtime.mapiternext

func mapiternext(it *hiter) {     ...     for ; i < bucketCnt; i++ {   ...   k := add(unsafe.Pointer(b), dataOffset+uintptr(offi)*uintptr(t.keysize))   v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+uintptr(offi)*uintptr(t.valuesize))   ...   if (b.tophash[offi] != evacuatedX && b.tophash[offi] != evacuatedY) ||    !(t.reflexivekey || alg.equal(k, k)) {    ...    it.key = k    it.value = v   } else {    rk, rv := mapaccessK(t, h, k)    if rk == nil {     continue // key has been deleted    }    it.key = rk    it.value = rv   }   it.bucket = bucket   if it.bptr != b {    it.bptr = b   }   it.i = i + 1   it.checkBucket = checkBucket   return  }  b = b.overflow(t)  i = 0  goto next }

到此,相信大家对“如何正确使用Go Map”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: 如何正确使用Go Map

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

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

猜你喜欢
  • 如何正确使用Go Map
    本篇内容主要讲解“如何正确使用Go Map”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何正确使用Go Map”吧!前言例子如下:func main() { &n...
    99+
    2023-06-15
  • 如何正确使用Go defer
    这篇文章主要介绍“如何正确使用Go defer”,在日常操作中,相信很多人在如何正确使用Go defer问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何正确使用Go defer”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-15
  • GO如何使用Mutex确保并发程序正确性
    这篇“GO如何使用Mutex确保并发程序正确性”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“GO如何使用Mutex确保并发程...
    99+
    2023-07-05
  • 如何在Go语言中正确使用包
    如何在Go语言中正确使用包,需要具体代码示例 在Go语言中,包(package)是组织和管理代码的基本单元。正确使用包可以帮助我们更好地组织代码,提高代码的复用性和可维护性。本文将介绍...
    99+
    2024-03-12
    使用 导入 包管理 go语言 标准库
  • golang如何正确删除map中的key
    在 Golang 中,map 是一个非常重要的数据类型,它可用于存储 key-value 对,十分灵活方便。然而,有时候我们需要从 map 中删除某个 key,以便我们能更好地管理和控制我们的数据。Map 是一种无序的集合,它和数组和切片不...
    99+
    2023-05-14
    go语言 Golang map
  • 如何正确使用MVCC
    这篇文章主要讲解了“如何正确使用MVCC”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何正确使用MVCC”吧! 简单理解版以下先引用我之前写过的...
    99+
    2024-04-02
  • 如何正确使用@property
    本篇内容主要讲解“如何正确使用@property”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何正确使用@property”吧!他们是这样说的:class People: ...
    99+
    2023-06-15
  • 如何正确使用WideCharToMultiByte
    要正确使用WideCharToMultiByte函数,需要按照以下步骤操作:1. 确定要转换的宽字符编码方式。WideCharToM...
    99+
    2023-09-26
    使用
  • 如何正确使用Go语言的缩进功能
    如何正确使用Go语言的缩进功能 在使用Go语言编写代码时,正确的缩进是非常重要的。良好的缩进可以提高代码的可读性,使代码结构清晰,便于维护和修改。本文将为大家介绍如何正确使用Go语言的...
    99+
    2024-04-02
  • 如何正确地在 Go 语言中使用空格
    Go 语言是一种简单、高效、并发性强的编程语言,它是由 Google 开发的一种开源语言。在 Go 语言中,空格的使用是非常重要的,它能够提高代码的可读性和易维护性。本文将介绍如何正确...
    99+
    2024-04-02
  • Go - 使用 pgtypes 的正确方法
    php小编西瓜今天为大家介绍一种正确的使用 pgtypes 的方法,这对于使用 Go 语言进行 PostgreSQL 数据库编程的开发者来说非常重要。pgtypes 是一个 Go 语言...
    99+
    2024-02-13
  • python如何正确使用yield
    目录生成器nextsendthrowclose使用场景大集合的生成简化代码结构协程与并发总结生成器 如果在一个方法内,包含了 yield 关键字,那么这个函数就是一个「生成器」。 生成器其实就是一个特殊的迭代器,它...
    99+
    2022-06-02
    python yield 使用yield
  • 如何正确的使用Gradle
    本篇文章为大家展示了如何正确的使用Gradle,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、Gradle相比Maven的优势配置简洁Maven是用pom.xml管理,引入一个jar包至少5行代码...
    99+
    2023-06-06
  • 如何正确的使用javascript
    本篇文章为大家展示了如何正确的使用javascript,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。使用javascript的方法:可以用script标签引入<script type...
    99+
    2023-06-14
  • 如何正确的使用springcloud
    这期内容当中小编将会给大家带来有关如何正确的使用springcloud,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、微服务简介 Ⅰ、我对微服务的理解微服务是软件开发的一种架构方式,由单一的应用小程序构...
    99+
    2023-06-14
  • 如何正确的使用Puppet
    今天就跟大家聊聊有关如何正确的使用Puppet,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1. 概述 puppet是一个开源的软件自动化配置和部署工具,它使用简单且功能强大,正得到...
    99+
    2023-06-12
  • 如何正确的使用pytest
    本篇文章为大家展示了如何正确的使用pytest,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1、安装pytest,打开dos窗口输入:pip install pytest2、通过pycharm工具下...
    99+
    2023-06-07
  • 如何正确使用注解
    本篇内容介绍了“如何正确使用注解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!日志脱敏场景简介在日志里我们的日志一般打印的是 model 的...
    99+
    2023-06-16
  • 如何正确的使用numpy.sum()
    这篇文章给大家介绍如何正确的使用numpy.sum(),内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。numpy的sum函数可接受的参数是:sum(a, axis=None, dtype=None,&...
    99+
    2023-06-15
  • 如何正确的使用TypeScript
    如何正确的使用TypeScript,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、TypeScript 是什么TypeScript 是一种由微软开发的自由和开源的编程语言...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作