1、散列表

看过HashMap源码的同学应该知道,HashMap是基于哈希表(散列表)的 Map 接口的非同步实现。

在我们put了一条key-value数据后,如下图,程序会先将key通过hash(key)这个函数转化成整形,这个整形做为数组的下标,插入数据或查找数据。

我们发现,散列表用的是数组支持按照下标随机访问数据的特性(时间复杂度O(1)),所以散列表其实就是数组的一种扩展,由数组演化而来。

我们通过散列函数的哈希算法(业界著名的有MD5、SHA、CRC等算法)把元素的键值映射为下标,然后将数据存储在数组中对应下标的位置。当我们按照键值查询元素时,我们用同样的散列函数,将键值转化数组下标,从对应的数组下标的位置取数据。

对于散列函数,实现的基本要求可以归纳为3点:

  1. 散列函数计算得到的散列值是一个非负整数;
  2. 如果 key1 = key2,那 hash(key1) == hash(key2) ;
  3. 如果 key1 ≠ key2,那 hash(key1) ≠ hash(key1) ;

对于第1点,因为数组下标从0开始;对于第2点,相同key,经过散列函数得到的值理应相同;但是对于第3点,会存在散列冲突的问题,既难以找到一个完美无冲突的算法,使得key不同时对应的散列值也不同【存在key1 ≠ key2,hash(key1) = hash(key1)的情况 】,即便是业界著名的哈希算法,也存在散列冲突的问题。

因此,对于散列冲突问题,可以其他低时间成本、低计算成本的途径解决

散列冲突解决方案

1. 开放寻址法

开放寻址法的核心思想是,如果出现了散列冲突,我们就重新探测一个空闲的位置。

开放寻址法解决方案有线性探测法、二次探测、双重散列等方案:

线性探测法(Linear Probing):
1)插入数据:当我们往散列表中插入数据时,如果某个数据经过散列函数之后,存储的位置已经被占用了,我们就从当前位置开始,依次往后查找(到底后从头开始),看是否有空闲位置,直到找到为止。

2)查找数据:我们通过散列函数求出要查找元素的键值对应的散列值,然后比较数组中下标为散列值的元素和要查找的元素是否相等,若相等,则说明就是我们要查找的元素;否则,就顺序往后依次查找。如果遍历到数组的空闲位置还未找到,就说明要查找的元素并没有在散列表中。

当然这里存在一个问题,就是存数据那块位置往前的某个数据被删除了,那么线性探索查到那块位置的时候就会判断元素不在散列表,查找就会失效,面对这个问题,我们在删除的时候,用下面删除的方法

3)删除数据:为了不让查找算法失效,可以将删除的元素特殊标记为deleted,当线性探测查找的时候,遇到标记为deleted的空间,并不是停下来,而是继续往下探测。

二次探测(Quadratic probing):线性探测每次探测的步长为1,即在数组中一个一个探测,而二次探测的步长变为原来的平方。

双重散列(Double hashing):使用一组散列函数,直到找到空闲位置为止。

线性探测法的性能描述:
用“装载因子”来表示空位多少,公式:散列表装载因子=填入表中的个数/散列表的长度。
装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

2. 链表法

散列表中,每个“桶(bucket)”都会对应一个条链表,在查找时先听过hash(key)找到位置,然后遍历链表找到对应元素

插入数据:当插入的时候,我们需要通过散列函数计算出对应的散列槽位,将其插入到对应的链表中即可,所以插入的时间复杂度为O(1)。
查找或删除数据:当查找、删除一个元素时,通过散列函数计算对应的槽,然后遍历链表查找或删除。对于散列比较均匀的散列函数,链表的节点个数k=n/m,其中n表示散列表中数据的个数,m表示散列表中槽的个数,所以是时间复杂度为O(k)。

HashMap就是用链表法解决散列冲突的问题

冲突解决方法对比

  优点 缺点 适用场景 案例
开放寻址法

1、数据存储在数组,可以有效利用CPU缓存加速查询速度

2、序列化简单

1、删除需要特殊标记已删除数据

2、所有数据存储在一个数组,发生冲突时,解决的代价更高,造成装载因子不能太大,使得更加浪费内存空间

1、数据量小

2、装载因子小

Java的ThreadLocalMap
链表法

1、内存利用率高,需要时再申请

2、对大装载因子容忍度高,可大于1

1、因为链表需要存储指针,存储指针需要消耗内存,不适合小对象存储(万一存储数据没有指针大???多浪费)

2、链表节点不是连续空间,因此CPU缓存不友好

1、存储大对象、大数据量的散列表

2、支持更多优化策略,如红黑树代替链表。

Java的LinkedHashMap

散列表及散列冲突解决方案相关推荐

  1. 查找——散列表的散列函数及冲突的处理办法

    查找 8.7 散列表 8.7.1 散列表的基本概念 8.7.2 散列函数 总结 8.7.3 处理冲突的闭散列方法--开地址方法 总结 闭散列方法的实现 8.7.4 处理冲突的开散列方法--链地址法 8 ...

  2. 字典:散列表、散列字典、关键字列表、集合与结构体

    字典 散列表和散列字典都实现了Dict的行为.Keyword模块也基本实现了,不同之处在于它支持重复键. Eunm.into可以将一种类型的收集映射转化成另一种. defmodule Sum dode ...

  3. 散列表--双散列、再散列与可扩散列

    双散列 我们将要考察的最后一个冲突解决方法是双散列(double hashing).对于双散列一种流行的选择是,意思是说冲突解决函数中又有一个对应X的散列函数,并在距离等处探测.选择不好将会是灾难性的 ...

  4. 散列表的删除与冲突处理 - 平方探测

    接下来我们看冲突处理中的平方探测法(终于写出来了- -.),平方探测顾名思义就是探测增量是1².-1².2².-2²-(线性探测是1.2.3.4--),平方探测与线性探测有点不同,线性探测是查找位置发 ...

  5. 【Java Swing/散列表】散列表实现电话号码查找系统——问题汇总分享

    说明:本文为我在做课设时遇到的问题汇总,解决方法多数来自网络,因深感到处搜索解决方法不易,所以做成汇总.因搜索时没有记录每个答案来源网站,如有侵权,请联系我.感谢无私分享的每一个人. 题目:设计散列表 ...

  6. 数据结构Java10【哈希表概述、散列函数的设计、散列冲突解决方案】

    学习地址:[数据结构与算法基础-java版]                  

  7. AcWing 840. 模拟散列表(散列hash)

    题目连接 https://www.acwing.com/problem/content/description/842/ 思路 使用开放寻址法,思路大概是这样我们讲要查询的数模上一个大于n的质数,然后 ...

  8. 散列表、散列函数和散列冲突以及应用场景(一文看懂)

    1.什么是散列表? 散列表(HashTable,也叫哈希表):由数组扩展而来,是根据键(Key)直接访问在内存存储位置的数据结构. 实现原理是:通过散列函数(也叫哈希函数)将元素的键(key)映射为数 ...

  9. 散列表的概念、构造方法及冲突处理

    简单来说查找算法,就是判断现有数据集合中是否有这个元素,或者是否有满足条件的元素.其中的 Hash 算法(散列表)则可以帮助我们判断是否有这个元素,虽然功能简单,但人家性能高啊.通过在记录的存储地址和 ...

最新文章

  1. python 获取 程序运行时间
  2. C++与MATLAB数组的存储结构
  3. android 自定义图片上传,android自定义ImageView仿图片上传(示例代码)
  4. OpenResty-Nginx安装目录介绍
  5. CSS的三种定位,成功入职字节跳动
  6. Python中的全局变量与局部变量2
  7. 东南大学数字信号处理实验_数字与信号处理实验1 离散时间信号分析
  8. 工作说明书(Statement of Work,简称SOW)
  9. mysql insert 运算_MySql insert插入操作的3个小技巧分享
  10. android开发框架_2019 年五大跨平台移动应用开发工具
  11. presto执行一条查询分为七步
  12. python显示文字框_python如何使用文本框
  13. xcap发包工具的简单使用1(构造报文)
  14. 所有的Python库,我都整理在这里了
  15. 环境试验的认识和环境可靠性测试
  16. 域名收敛和域名发散(分散)
  17. 云服务器系统么开始bios,云服务器怎么进入bios
  18. java-net-php-python-jspm服装类跨境电商库存控制计算机毕业设计程序
  19. python3 正则匹配特殊符号_Python3 正则表达式特殊符号及用法(详细列表)
  20. 解决win10学习汇编工具的烦恼——汇编Debug的下载和使用(包含可用下载链接)

热门文章

  1. js中元素样式设置的六种方法
  2. 农历公历万年历互查系统
  3. Redis——发布/订阅
  4. Xcode自带的超好用的诊断工具
  5. 热力地图高德_高德地图
  6. C语言实现可伸缩的栈结构
  7. 目前国内常用的无纸化会议系统——迅控无纸化
  8. PHP流量卡发货查单系统源码 流量卡物流发货运单号查询平台 一键安装版
  9. 读jquery 权威指南[3]-动画
  10. docker中镜像各ID之间的关系和计算-layerID-diffID-chainID-cacheID的计算