
一、利用下标法:
m1[str] = val
先查找有没有str这个元素,如有,不作任何 *** 作,没有的话,添加str并给str关联的对象赋值也可以只写m1[str]
二、利用函数
m1.insert(e),e为pair型,即,val_type,如果,e.fitst不在map中,刚添加e
m1.insert(beg, end) beg, end, 为元素迭代器,必须是val_tyle型
m1.insert(iter, e) 如果e.first不在m中,创建新元素,并以iter为起点找新元素的位置.
楼上的比我快一步,不好意思!
用的是c++ map的insert方法。
函数定义:
single element (1) 插入单个元素 队尾插入
pair<iterator,bool>insert (const value_type&val)
with hint (2) 插入单个元素 在position的位置插入
iterator insert (iterator position, const value_type&val)
range (3) 插入一串元素 一般用的是另一个map中的,从开始到结束
template <class InputIterator> void insert (InputIterator first, InputIterator last)
示例:
// map::insert (C++98)#include <iostream>
#include <map>
int main ()
{
std::map<char,int> mymap
// first insert function version (single parameter):第1种
mymap.insert ( std::pair<char,int>('a',100) )
mymap.insert ( std::pair<char,int>('z',200) )
std::pair<std::map<char,int>::iterator,bool> ret
ret = mymap.insert ( std::pair<char,int>('z',500) )
if (ret.second==false) {
std::cout << "element 'z' already existed"
std::cout << " with a value of " << ret.first->second << '\n'
}
// second insert function version (with hint position):第2种
std::map<char,int>::iterator it = mymap.begin()
mymap.insert (it, std::pair<char,int>('b',300)) // max efficiency inserting
mymap.insert (it, std::pair<char,int>('c',400)) // no max efficiency inserting
// third insert function version (range insertion):第3种
std::map<char,int> anothermap
anothermap.insert(mymap.begin(),mymap.find('c'))
// showing contents:
std::cout << "mymap contains:\n"
for (it=mymap.begin() it!=mymap.end() ++it)
std::cout << it->first << " => " << it->second << '\n'
std::cout << "anothermap contains:\n"
for (it=anothermap.begin() it!=anothermap.end() ++it)
std::cout << it->first << " => " << it->second << '\n'
return 0
}
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开始。 从前往后遍历,再次遍历到起始位置时,遍历完成。
https://www.qcrao.com/2019/05/22/dive-into-go-map/
https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-hashmap/
https://www.bilibili.com/video/BV1Q4411W7MR?spm_id_from=333.337.search-card.all.click
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)