go语言中map怎么用 golang map使用
golang hashmap的使用及实现
由于go语言是一个强类型的语言,因此hashmap也是有类型的,具体体现在key和value都必须指定类型,比如声明一个key为string,value也是string的map,
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:主机域名、网站空间、营销软件、网站建设、綦江网站维护、网站推广。
需要这样做
大部分类型都能做key,某些类型是不能的,共同的特点是: 不能使用== 来比较,包括: slice, map, function
在迭代的过程中是可以对map进行删除和更新操作的,规则如下:
golang的map是hash结构的,意味着平均访问时间是O(1)的。同传统的hashmap一样,由一个个bucket组成:
那我们怎么访问到对应的bucket呢,我们需要得到对应key的hash值
各个参数的意思:
目前采用的是这一行:
| 6.50 | 20.90 | 10.79 | 4.25 | 6.50 |
golang map源码浅析
golang 中 map的实现结构为: 哈希表 + 链表。 其中链表,作用是当发生hash冲突时,拉链法生成的结点。
可以看到, []bmap 是一个hash table, 每一个 bmap是我们常说的“桶”。 经过hash 函数计算出来相同的hash值, 放到相同的桶中。 一个 bmap中可以存放 8个 元素, 如果多出8个,则生成新的结点,尾接到队尾。
以上是只是静态文件 src/runtime/map.go 中的定义。 实际上编译期间会给它加料 ,动态地创建一个新的结构:
上图就是 bmap的内存模型, HOB Hash 指的就是 top hash。 注意到 key 和 value 是各自放在一起的,并不是 key/value/key/value/... 这样的形式。源码里说明这样的好处是在某些情况下可以省略掉 padding 字段,节省内存空间。
每个 bmap设计成 最多只能放 8 个 key-value 对 ,如果有第 9 个 key-value 落入当前的 bmap,那就需要再构建一个 bmap,通过 overflow 指针连接起来。
map创建方法:
我们实际上是通过调用的 makemap ,来创建map的。实际工作只是初始化了hmap中的各种字段,如:设置B的大小, 设置hash 种子 hash 0.
注意 :
makemap 返回是*hmap 指针, 即 map 是引用对象, 对map的操作会影响到结构体内部 。
使用方式
对应的是下面两种方法
map的key的类型,实现了自己的hash 方式。每种类型实现hash函数方式不一样。
key 经过哈希计算后得到hash值,共 64 个 bit 位。 其中后B 个bit位置, 用来定位当前元素落在哪一个桶里, 高8个bit 为当前 hash 值的top hash。 实际上定位key的过程是一个双重循环的过程, 外层循环遍历 所有的overflow, 内层循环遍历 当前bmap 中的 8个元素 。
举例说明: 如果当前 B 的值为 5, 那么buckets 的长度 为 2^5 = 32。假设有个key 经过hash函数计算后,得到的hash结果为:
外层遍历bucket 中的链表
内层循环遍历 bmap中的8个 cell
建议先不看此部分内容,看完后续 修改 map中元素 - 扩容 操作后 再回头看此部分内容。
扩容前的数据:
等量扩容后的数据:
等量扩容后,查找方式和原本相同, 不多做赘述。
两倍扩容后的数据
两倍扩容后,oldbuckets 的元素,可能被分配成了两部分。查找顺序如下:
此处只分析 mapaccess1 ,。 mapaccess2 相比 mapaccess1 多添加了是否找到的bool值, 有兴趣可自行看一下。
使用方式:
步骤如下:
扩容条件 :
扩容的标识 : h.oldbuckets != nil
假设当前定位到了新的buckets的3号桶中,首先会判断oldbuckets中的对应的桶有没有被搬迁过。 如果搬迁过了,不需要看原来的桶了,直接遍历新的buckets的3号桶。
扩容前:
等量扩容结果
双倍扩容会将old buckets上的元素分配到x, y两个部key 1 B == 0 分配到x部分,key 1 B == 1 分配到y部分
注意: 当前只对双倍扩容描述, 等量扩容只是重新填充了一下元素, 相对位置没有改变。
假设当前map 的B == 5,原本元素经过hash函数计算的 hash 值为:
因为双倍扩容之后 B = B + 1,此时B == 6。key 1 B == 1, 即 当前元素rehash到高位,新buckets中 y 部分. 否则 key 1 B == 0 则rehash到低位,即x 部分。
使用方式:
可以看到,每一遍历生成迭代器的时候,会随机选取一个bucket 以及 一个cell开始。 从前往后遍历,再次遍历到起始位置时,遍历完成。
golang变量(二)——map和slice详解
衍生类型,interface{} , map, [] ,struct等
map类似于java的hashmap,python的dict,php的hash array。
常规的for循环,可以用for k,v :=range m {}. 但在下面清空有一个坑注意:
著名的map[string]*struct 副本问题
结果:
Go 中不存在引用传递,所有的参数传递都是值传递,而map是等同于指针类型的,所以在把map变量传递给函数时,函数对map的修改,也会实质改变map的值。
slice类似于其他语言的数组(list,array),slice初始化和map一样,这里不在重复
除了Pointer数组外,len表示使用长度,cap是总容量,make([]int, len, cap)可以预申请 比较大的容量,这样可以减少容量拓展的消耗,前提是要用到。
cap是计算切片容量,len是计算变量长度的,两者不一样。具体例子如下:
结果:
分析:cap是计算当前slice已分配的容量大小,采用的是预分配的伙伴算法(当容量满时,拓展分配一倍的容量)。
append是slice非常常用的函数,用于添加数据到slice中,但如果使用不好,会有下面的问题:
预期是[1 2 3 4 5 6 7 8 9 10], [1 2 3 4 5 6 7 8 9 10 11 12],但实际结果是:
注意slice是值传递,修改一下:
输出如下:
== 只能用于判断常规数据类型,无法使用用于slice和map判断,用于判断map和slice可以使用reflect.DeepEqual,这个函数用了递归来判断每层的k,v是否一致。
当然还有其他方式,比如转换成json,但小心有一些异常的bug,比如html编码,具体这个json问题,待后面在分析。
分享文章:go语言中map怎么用 golang map使用
当前URL:http://ybzwz.com/article/ddohpsh.html