map
- map 为一个hash table的引用
- 不能对map中的值取地址
- map的index需要可比较,如果想用无法比较的值作为key,可以把key序列化为字符串
- map可以在迭代的过程中删除元素,迭代器不会失效
- golang中没有set,可以用map实现set功能,用key存放 set中的值,val中存放bool类型形如map[int]bool
for range 遍历map使用注意点
链接
hash函数
哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
哈希函数的特点
- 相同的输入一定得到相同的输出;
哈希函数内部映射规则不变,y=f(x),x相同,y也相同- 不同的输入大概率得到不同的输出
输出hash值为一个有界值,当输入无限时,输出必然会产生相同。输入不同,但hash值相同的现象也叫做hash碰撞。hash碰撞不可避免,我们只能从工程上解决hash碰撞的问题
哈希表
hash表定义
hash表也叫散列表。是根据键(Key)而直接访问在内存储存位置的数据结构。也就是说,我们存储数据时需要提供key和val,通过hash算法对key求hash值,然后映射到数组上(一般规则为hash_val %array_len = array_index)
hash碰撞的解决方法
冲突解决方法图片以及内容来源 如有版权问题,请联系我删除
开放地址法
基本思想:当我们向当前哈希表写入新的数据时,如果发生了冲突,就会将键值对写入到下一个索引不为空的位
[图非原创]
图片分析
如图可见hash表中存放的为pair对{key,value}。
存入{key1,value}时 :
- 求出数组下标 : array_index= hash(key)%array_len
- 赋值: array[array_index] = {key1,value}
存入{key2,value}时,
假设: hash(key2) != hash(key1) && hash(key) % array_len == hash(key) % array_len
此时根据key2求出的array_index,与key1求出的array_index相同,插入数据时我们需要认为判断pair对{key,value}是否为同一个,如果不是则向后找到第一个索引不为空(非越界)的位置插入
存入{key3,value}时,
假设: hash(key3) == hash(key1)
和插入{key2,value}相同
拉链法
基本思想: 实现拉链法一般会使用数组加上链表,不过一些编程语言会在拉链法的哈希中引入红黑树以优化性能,拉链法会使用链表数组作为哈希底层的数据结构,我们可以将它看成可以扩展的二维数组
[图非原创]
两种方式实现hash表比较
在实现相同规模存储的情况下,开放地址法需要的空间大于拉链法(数组内存)
数组大小一样,且存储数据小于数组容量,且不发生碰撞时,由于拉链法需要动态分配内存,创建变量位于堆上,读写速率慢于开放地址法。
实际应用中,更多会选择拉链法实现hash表
map中元素不可寻址的原因
装载因子 = 元素数量 / 数组长度
装载因子越大,hash表读写性能越差
当装载因子过大时,golang map会重新分配内存空间,旧地址会失效,所以golang中map禁止了map元素的寻址
评价hash算法
由hash表实现我们可以知道,hash碰撞增加了hash表的时间复杂度。碰撞概率小就成了hash算法优劣评价的一个指标。如何让碰撞概率小?让hash结果更加均匀
所以hash值分布越均匀,则该hash算法越好