文章目录

  • 一、哈希表简介
  • 二、解决Hash冲突的方式
    • 1.线性探测法:
    • 2.链地址法(拉链法):
  • 三、代码实现
  • 四、效果演示
  • 更多学习资料请看这里

一、哈希表简介

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

记录的存储位置=f(关键字)

这里的对应关系f称为散列函数,又称为哈希(Hash函数),采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。

哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。(或者:把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。)
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。

数组的特点是:寻址容易,插入和删除困难;
而链表的特点是:寻址困难,插入和删除容易。
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组”,如图:

左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

二、解决Hash冲突的方式

1.线性探测法:

如果hash函数计算得出的存储位置一杯占用,那么将继续向后寻找空间的位置进行存放
缺点:hash一旦冲突,将会增加查询的时间复杂度,由原来的O(1)转化成O(n),hash冲出越多,效率越低

随着hash表越来越满,哈希冲的概率就越大,所以的哈希表都有一个衡量因子,即加载因子(loadfactor = 0.75)

公式: 以占有的hash位置 / 总的hash位置 > loadfactor O(1)

2.链地址法(拉链法):

把哈希冲突冲突的元素都放在一个链表中,但是链表的长度不能太长,越长效率越慢

时刻关注哈希表的loadfactor加载因子,如果查过及时进行扩容操作,扩容后原来表的hash数据,需要重新hash
如图:

三、代码实现

public class HashMap<K,V> {/*** hash表定义*/private ArrayList<LinkedList<Entry<K,V>>> mapList;/*** 加载因子 以占有的个数 / 总的个数*/private double loadfactor = 0.75;/*** 记录已使用的个数*/private int usedBuckets = 0;/*** 素数表*/private static int [] primeTable = {3,7,23,47,97,251,443,911};/*** 素数表的索引*/private int primeTableIndex = 0;/*** 哈希表初始化*/public HashMap() {this(0.75);}/***  自定义加载因子* @param loadfactor*/public HashMap(double loadfactor) {this.mapList = new ArrayList<>(primeTable[primeTableIndex]);for (int i = 0; i < primeTable[primeTableIndex]; i++) {this.mapList.add(new LinkedList<Entry<K,V>>());}this.loadfactor = loadfactor;}/*** 打包键值对* @param <K> key* @param <V> value*/static class Entry<K,V>{K key;V value;public Entry(K key, V value) {this.key = key;this.value = value;}}/*** 增加一个键值对*  不允许添加key : null的元素*  不允许key重复*  key存在则覆盖,不存在则新增* @param key* @param value*/public void put(K key ,V value){//1.扩充处理//计算现在的长度double lf = this.usedBuckets * 1.0 / this.mapList.size();System.out.println("加载因子:"+ lf +"----"+"哈希表的长度:"+this.mapList.size());//需要扩容if(lf > this.loadfactor){resize();}//2. 计算key的散列码int index = key.hashCode() % mapList.size();LinkedList<Entry<K, V>> list = mapList.get(index);if(list.isEmpty()){//index 位置为空,未被占用list.add(new Entry<K,V>(key,value));//位置被占用this.usedBuckets ++;}else {for (Entry<K,V> entry:list) {//key 存在,直接替换valueif(entry.key.equals(key)){entry.value = value;return;}}//key 不存在,新增 key valuelist.add(new Entry<K,V>(key,value));}}/*** hash 删除* @param key* @return*/public V remove(K key){//1. 计算key的散列码int index = key.hashCode() % mapList.size();LinkedList<Entry<K, V>> list = mapList.get(index);//2. 不存在if(list.isEmpty()){return null;}//3. 存在 删除Iterator<Entry<K, V>> it = list.iterator();while (it.hasNext()){Entry<K, V> entry = it.next();if(entry.key.equals(key)){V value = entry.value;it.remove();return value;}}return null;}/*** hash 查询* @param key* @return*/public V get(K key){//1. 计算key的散列码int index = key.hashCode() % mapList.size();LinkedList<Entry<K, V>> list = mapList.get(index);//2. 不存在if(list.isEmpty()){return null;}//3. 存在 删除Iterator<Entry<K, V>> it = list.iterator();while (it.hasNext()){Entry<K, V> entry = it.next();if(entry.key.equals(key)){V value = entry.value;return value;}}return null;}/*** 哈希表的扩容操作 ---HashMap transfer转移节点的函数*/private void resize(){//oldBucketsList 指向原来的 hash表ArrayList<LinkedList<Entry<K,V>>> oldBucketsList = mapList;//将个数归 0this.usedBuckets = 0;//已达到最大扩容数量if(primeTableIndex == this.primeTable.length){return;}//重新开辟容量this.mapList = new ArrayList<>(primeTable[++primeTableIndex]);for (int i = 0; i < primeTable[primeTableIndex]; i++) {this.mapList.add(new LinkedList<Entry<K,V>>());}//遍历原来的数据,存储到新的哈希表中for (LinkedList<Entry<K,V>> oldList:oldBucketsList ) {if(oldList.size() >0 ){//把key value 添加到新的hash表中for(Entry<K,V> entry : oldList){//2. 计算key的散列码int index = entry.key.hashCode() % mapList.size();LinkedList<Entry<K, V>> list = mapList.get(index);if(list.isEmpty()){//位置被占用this.usedBuckets ++;}list.add(new Entry<K,V>(entry.key,entry.value));}}}}

四、效果演示

这里的HashMap是上面自定义的,请不要引用错

   public static void main(String[] args) {HashMap<Integer,String> map = new HashMap<>();map.put(1,"aaaa");map.put(2,"bbbb");map.put(3,"cccc");map.put(4,"dddd");map.put(5,"eeee");map.put(6,"eefee");
//        System.out.println(map.get(1));
//        System.out.println(map.get(2));
//        System.out.println(map.get(3));
//
//        map.put(1,"cccc");
//        System.out.println(map.get(1));
//
//        map.remove(2);
//        System.out.println(map.get(2));}

更多学习资料请看这里

SpringCloud学习代码: https://github.com/Dylan-haiji/javayh-cloud

Redis、Mongo、Rabbitmq、Kafka学习代码: https://github.com/Dylan-haiji/javayh-middleware

AlibabaCloud学习代码:https://github.com/Dylan-haiji/javayh-cloud-nacos

哈希冲突如何解决,请看这里!相关推荐

  1. 哈希 ---《哈希函数》------除数的选取为什么是质数?、《哈希冲突》------解决方法、《闭散列》、《开散列》

    一.哈希概念 顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较**.顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN ) ...

  2. Algorithms_算法专项_Hash算法的原理哈希冲突的解决办法

    文章目录 引导案例 案例一 案例二 hash表(散列表) 哈希函数(散列函数) 哈希碰撞( 哈希冲突 ) 如何解决hash冲突(hash碰撞) 开放寻址 线性探测(LP) 二次探测 (平方探测 QP) ...

  3. 哈希表及哈希冲突的解决

    哈希表(也叫关联数组)一种通用的数据结构,哈希表是一种通过关键码去寻找值得数据映射结构 例:新华字典.如果我想知道"按"的详细信息,根据拼音去查找拼音索引,首先查找"an ...

  4. 哈希冲突与解决哈希冲突的两种方法

    哈希冲突与解决哈希冲突的两种方法 1.哈希冲突 2.解决哈希冲突的方法 (1)链接法 (2)开放寻址法 ①线性探查 ②二次探查 ③双重探查 注:本文注重对解决哈希冲突方法的介绍,而非对背后原理的介绍. ...

  5. 什么是哈希表?为什么要使用哈希表?哈希表的实现原理?哈希冲突怎么解决?

    前言 当我们在编程过程中,往往需要对线性表进行查找操作.在顺序表中查找时,需要从表头开始,依次遍历比较a[i]与key的值是否相等,直到相等才返回索引i:在有序表中查找时,我们经常使用的是二分查找,通 ...

  6. 哈希表(哈希函数的设计与哈希冲突的解决)

    文章目录 一.什么是哈希表 二.哈希函数 三.哈希冲突的原因与解决方法 1.数组扩容 2.一个优秀的哈希函数 3.开放寻址法 4.链表法 四.总结 一.什么是哈希表 哈希表就是数组+哈希函数,其核心思 ...

  7. 哈希冲突常用解决方法

    文章目录 1.基本概念 2.解决哈希冲突的方法 2.1 开放寻址法 2.1.1 线性探查法 2.1.2 平方探查法 2.1.3 双散列函数探查法 2.1.4 伪随机探查法 2.1.5 小结 2.2 链 ...

  8. 什么是哈希冲突?哈希冲突怎么解决?

    一.哈希表 哈希表是一种以键对应值(key-indexed) 来存储数据的结构,只要输入要查找的键即key,即可查找到对应的值. 将键作为索引,这样就可以快速访问任意键的值. 1.1 哈希函数 哈希函 ...

  9. 哈希冲突、哈希冲突的解决方法

    首先,什么是哈希表?什么又是哈希冲突? ①哈希表是基于数组的一种存储方式.它主要由哈希函数和数组构成.当要存储一个数据的时候,首先用一个函数计算数据的地址,然后再将数据存进指定地址位置的数组里面.这个 ...

最新文章

  1. 国外十大最流行PHP框架排名
  2. 一文全览,AAAI 2020上的知识图谱
  3. c++中的list用法
  4. 【小白学习C++ 教程】十四、C++ 中预处理器
  5. 为什么Spring的健康状况会再次下降,下降,上升,上升,上升和下降?
  6. Opencv实现利用滑动条来调整阈值
  7. vector的基本操作简析
  8. 软件开发有哪些规范和标准_高品质、标准化——四维图新自动驾驶地图通过ASPICE CL2认证...
  9. ORB-SLAM2学习2 KeyFrame.h
  10. 商务统计_13 使用excel拟合曲趋势线
  11. dnf php的补丁放哪,DNF模型文件在哪 补丁玩家必备知识
  12. Python数据分析通关,30个案例!
  13. Js加载百度热点新闻
  14. python发邮件图片太长显示不出来_小白入门,用python 发送定时邮件,将Dataframe转为邮件正文,链接显示为图片...
  15. random.shuffle(lst)
  16. python爬取微博话题下面的帖子并存入excel文件
  17. Python操作*.cfg配置文件
  18. html5 放风筝,萧山5个放风筝绝佳地
  19. 原生js由html创建节点,[js高手之路]HTML标签解释成DOM节点的实现方法
  20. ubuntu 部署STF

热门文章

  1. shell 编程arry数组
  2. 【UE4】Slomo 顿帧
  3. java前端传值,后端接收值为null
  4. mysql 最大连接_MySQL最大连接数设置
  5. Win10安装nginx笔记
  6. 将乱序字母按A-Z顺序排序
  7. 关于时间格式 2016-08-9T10:01:54.123Z 20160809100154.123Z 处理方法
  8. nuxt 怎么获取cookie
  9. HDU3138 Coconuts(最小割)
  10. (转)iptables设置Linux全局代理