哈希表及其与Java类集的关系

news/2024/5/20 3:09:23/文章来源:https://blog.csdn.net/chenchenchencl/article/details/128328387

目录

1.哈希表的概念

2.哈希冲突

3.如何避免哈希冲突? 

3.1哈希函数设计

3.2 负载因子的调节

4.解决哈希冲突

4.1闭散列

4.1.1线性探测

4.1.2二次探测

4.2开散列(哈希桶)

5.HashMap

6.HashSet


1.哈希表的概念

假设有一组数据,要让你去搜索其中的一个关键码,这种场景是很常见的,不同的数据结构的搜索的效率是不同的.顺序结构及平衡树中,元素关键码与其存储的位置之间没有对应关系,因此在查找一个元素时,必须要经过关键码的多次比较.

顺序查找时间复杂度为O(N),平衡树查找时间复杂度是O(log2 N),搜索的效率取决于搜索过程中比较的次数! 

哈希表这个数据结构可以做到不经过任何比较,一次直接从表中得到要搜索的元素.

哈希表通过构造哈希函数使元素与元素存储位置之间能够建立一一映射的关系,那么查找的时间复杂度就是O(1).

向该结构插入元素时,根据待插入的元素的关键码,以哈希函数计算出元素存储的位置进行存放

搜索元素时同样方式,把求得的函数值作为元素的存储位置,在结构中按此位置取元素比较,相等则搜索成功

这种方式就是哈希(散列)方法,使用的函数称为哈希函数(散列函数),构造出来的结构称为哈希表结构(散列表)

2.哈希冲突

了解哈希冲突之前先看一个哈希表存储的例子 :
数据集合{1,5,9,8,6};

哈希函数:hash(key) = key % capacity;capacity为存储元素底层空间的总大小

 经过哈希函数的计算,1%10 = 1,5%10 = 5,9%10 = 9,8%10 = 8,6%10 = 6.

这样就将数据集合存进了哈希表,在搜索的时候不必进行多次比较,搜索效率大大提高

但是效率高的同时,也会出现其他问题,如果在上述哈希表中插入55,会出现什么问题?

我们发现哈希值是相同的,但是5下标已经存储了5,55没位置存了,即不同关键字通过相同的哈希函数计算出来相同的哈希地址,这种现象被称为哈希冲突.把具有相同哈希地址不同关键码的数据元素称为"同义词"

3.如何避免哈希冲突? 

 由于哈希表底层数组的容量往往下小于实际要存的关键字的数量,导致了哈希冲突的发生是必然的,我们能做的就是尽量降低哈希冲突率 


3.1哈希函数设计

引起哈希冲突可能是因为哈希函数设计不够合理

哈希函数设计的原则:

函数定义域必须包括需要存储的全部关键码,散列表允许有m个地址时,值域必须在0-m-1之间

哈希函数计算出来的哈希地址能均匀分布在整个空间中

哈希函数应该比较简单

常见的哈希函数:

1.直接定制法

取关键字的某个线性函数为哈希地址:hash(key) = A*key+B

优点:简单均匀

缺点:需要先知道关键字分布情况

使用场景:适合查找较小且连续的情况

2.除留余数法

设散列表中允许的地址为m,取一个质数p(p<=m),按照hash(key) = key%p,将关键码转换成哈希地址

哈希函数设计的越精妙,越能降低哈希冲突率,但不能避免哈希冲突

3.2 负载因子的调节

散列表负载因子定义: \alpha = 表中元素个数/散列表的长度

\alpha与表中元素个数成正比, \alpha越大,元素个数越多,反之, \alpha越小,表中元素个数越少,哈希冲突概率越低

Java的系统库限制了负载因子为0.75,超过这个值将resize散列表

负载因子和冲突率的关系演示

 因此当冲突率达到一定程度时,我们要通过降低负载因子来降低冲突率

哈希表的关键字个数是不变的,因此只能控制散列表的长度来降低冲突率

4.解决哈希冲突

在上述优化都不能有效降低哈希冲突率时,我们就要引入其他办法来解决哈希冲突

4.1闭散列

闭散列也叫开放定址法,当发生哈希冲突时,如果哈希表没有被装满,说明在哈希表中还有其他位置,那么可以将key放到冲突位置的"下一个"位置,寻找下一个位置有两种探测方法

4.1.1线性探测

在上面的场景中,要插入55元素,通过计算哈希地址为5,但是5位置已经被5填入了,即发生哈希冲突

线性探测法:从发生哈希冲突的位置一次向后寻找,找到一个空位置填入待插入元素

缺点

采用闭散列方法解决哈希冲突时要注意不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索,如果突然删除5,那么搜索55时,就找不到了,因此线性探测法要删除一个元素采用标记的伪删除法来删除一个元素

线性探测还会把冲突的元素都放在一起,如果要放入65,75,95,那么这些元素会被堆积在相邻有空的位置

4.1.2二次探测

为了解决线性探测将冲突数据堆积在一起的缺点,二次探测提供了寻找空位置的方法

或者

其中i = 1,2,3......i表示某个位置第几次重复哈希冲突

H0是通过散列函数Hash(x)对关键码key进行计算得到的位置

m表示表的大小 

如果放入55,65,75

 有效的解决了线性探测将冲突元素堆积的情况

当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能插入,而且任何一个位置都不会被探查两次.因此只要表中有一半的空位置,就不会存在表满的问题,在搜索时可以不考虑装满的情况,但在插入时必须确保表的负载因子<=0.5,如果超出,先扩容

删除时也是采用标记的方法删除

这也造成了闭散列的一个缺点:空间里利用率低

4.2开散列(哈希桶)

这是Java中的HashMap用到的解决哈希冲突的方式

 开散列法又叫开链法(链地址法),首先对关键码的集合用散列函数计算散列地址,具有相同的地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过单链表链接起来,链表头节点存在哈希表中!

 对于上述的插入55,65,75

 上图中开散列中每个桶放的都是发生哈希冲突的元素

开散列可以认为是把一个在大集合中的搜索问题,转化成在小集合中做搜索了

Java的HashMap中就是用这种数组加链表的方式解决哈希冲突,当数组长度超过64并且链表长度超过8的时候,就会变成红黑树,链表如果一直变长,那么搜索效率还是降低,链表的搜索效率是O(N),转化成红黑树存储,效率会提升!!

性能分析: 虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的, 也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是 O(1) 

5.HashMap

我们使用HashMap实例化Map

public class Test {public static void main(String[] args) {HashMap<String,Integer> map = new HashMap<>();map.put("zhangsan",12);map.put("lisi",13);System.out.println(map);}
}

结果顺序不是我们put的顺序,是因为在put时,计算出的哈希值是不一样的,存放的位置就不同于put的顺序,原因不是比较造成的,是哈希出的地址不同

传入不可比较对象时,也可以添加

class Student{}
public class Test {public static void main(String[] args) {HashMap<Student,Integer> map = new HashMap<>();map.put(new Student(),12);map.put(new Student(),13);System.out.println(map);}
}

添加元素时没有对象的比较,因此可以添加null值为key

public class Test {public static void main(String[] args) {HashMap<Student,Integer> map = new HashMap<>();map.put(null,null);System.out.println(map);}
}

 

HashMap中不会存在相同的key,key相同则更新value

public class Test {public static void main(String[] args) {HashMap<String,Integer> map = new HashMap<>();map.put("hello",12);map.put("hello",22);System.out.println(map);}
}

 HashMap的方法和TreeMap介绍的方法是相同的

使用HashMap统计一组数据中,每个数据出现的次数

public static void main(String[] args) {HashMap<Integer,Integer> map = new HashMap<>();int[] arr = {1,3,4,2,1,3,4};for (int i = 0; i < arr.length; i++) {int key = arr[i];if(map.get(key)==null){map.put(arr[i],1);}else{int val = map.get(key);//key出现的次数map.put(key,val+1);}}System.out.println("");for (Map.Entry<Integer,Integer> entry:map.entrySet()) {System.out.println("key: "+entry.getKey()+"出现次数: "+entry.getValue());}}

6.HashSet

使用HashSet实例化Set

public class Test {public static void main(String[] args) {HashSet<Integer> set = new HashSet<>();set.add(1);set.add(1);System.out.println("size: "+set.size());}
}

 HashSet也不能重复添加元素,是因为底层也是一个HashMap,来保证key是唯一的

value是一个默认值

HashSet有去重的功能,使用HashSet 统计一组数据中,第一个重复的数

    public static void main(String[] args) {HashSet<Integer> set = new HashSet<>();int[] arr = {5,3,4,2,1,3,4};for (int i = 0; i < arr.length; i++) {if(!set.contains(arr[i])){set.add(arr[i]);}else{System.out.println(arr[i]);break;}}}

在Map和Set一文中使用TreeSet对数据进行去重,使用HashSet也可以,并且效率更高

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_236281.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式软件工程师技能树——Linux应用编程+网络编程+驱动开发+操作系统+计算机网络

文章目录Linux驱动开发1、Linux内核组成2、用户空间与内核的通讯方式有哪些&#xff1f;3、系统调用read/write流程4、内核态用户态的区别5、bootloader内核 根文件的关系6、BootLoader的作用7、BootLoader两个启动阶段1、汇编实现&#xff0c;完成依赖于CPU体系架构的设置&…

异常检测方法总结

在数据挖掘中&#xff0c;异常检测&#xff08;英语&#xff1a;anomaly detection&#xff09;对不匹配预期模式或数据集中其他项目的项目、事件或观测值的识别。 通常异常项目会转变成银行欺诈、结构缺陷、医疗问题、文本错误等类型的问题。异常也被称为离群值、新奇、噪声、…

[Android移动安全渗透基础教程] 易受攻击的移动应用程序

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 0x01 如何设置 GoatDroid (FourGoats) 1.1 简介&#xff08;概述&#…

第十四届蓝桥杯集训——JavaC组第十三篇——for循环

第十四届蓝桥杯集训——JavaC组第十三篇——for循环 目录 第十四届蓝桥杯集训——JavaC组第十三篇——for循环 for循环(重点) 倒序迭代器 for循环死循环 for循环示例 暴力循环 等差数列求和公式 基础循环展开 循环控制语句 break结束 continue继续 for循环(重点) f…

MySQL主从复制太慢,怎么办?

本文分析了MySQL主从延迟的原因以及介绍了MTS方案。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达mysql主从同步延迟原因导致备库延迟的原因主要有如下几种&#xff1a;通常备库所在机器的性能要比主库所在的机器性能差&#xff0c;执行…

nodejs092学生考勤请假管理系统vue

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.3 B/S结构 4 2.4 MySQL数据库 4 前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统…

ChatGpt详细注册流程

ChatGpt详细注册流程ChatGpt的网址&#xff1a;直接点击我 点击链接后向下滑动看到TRY CHATGPT如下图所示&#xff1a; 点击TRY CHATGPT后会跳转如下图界面&#xff1a; 点击Log in(登录)如下图&#xff1a; 因为首次登录你肯定是没有账号的所以需要先点击红框框出的Sign up…

七个步骤覆盖 API 接口测试

接口测试作为最常用的集成测试方法的一部分&#xff0c;通过直接调用被测试的接口来确定系统在功能性、可靠性、安全性和性能方面是否能达到预期&#xff0c;有些情况是功能测试无法覆盖的&#xff0c;所以接口测试是非常必要的。首先需要对接口测试的基本信息做一些了解&#…

Java+Swing实现的五子棋游戏

JavaSwing实现的五子棋游戏一、系统介绍二、功能展示1.游戏展示三、系统实现1.ChessFrame .java四、其它1.其他系统实现2.获取源码一、系统介绍 五子棋游戏实现人机对战、人人对战两个模式。 二、功能展示 1.游戏展示 三、系统实现 1.ChessFrame .java package five;impor…

JDK的使用——Java开发第一步

JDK的使用——Java开发第一步 1 什么是JDK JDK是 Java 语言的软件开发工具包&#xff0c;是整个java开发的核心&#xff0c;使用Java开发第一步就是要在计算机上安装JDK。 JDK主要包含三个部分&#xff1a; 1 JAVA开发工具(jdk\bin) 2 基础开发库(jdk\jre\lib) 3 基础开发库…

java TCP发送数据

TCP是一种可靠的网络协议 他在通信的两端都建立了Socke对象 从而形成了两端的虚拟链路 一旦建立了虚拟链路 两端就可以通过链路通信 TCP会将通信两端分为 客户端和服务端 客户端通过Socke实现 服务端通过ServerSocke实现 那么 我们就来实现一下发送数据的方法 我们创建一个测…

【C语言数据结构(基础版)】第三站:链表(二)

目录 一、单链表的缺陷以及双向链表的引入 1.单链表的缺陷 2.双向链表的引入 3.八大链表结构 &#xff08;1&#xff09;单向和双向 &#xff08;2&#xff09;带头和不带头 &#xff08;3&#xff09;循环和不循环 &#xff08;4&#xff09;八种链表结构 二、带头双向…

web网页设计期末课程大作业:美食餐饮文化主题网站设计——HTML+CSS+JavaScript美食餐厅网站设计与实现 11页面

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

[附源码]Node.js计算机毕业设计高校教学过程管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

java毕业设计——基于java+Socket+sqlserver的网络通讯系统设计与实现(毕业论文+程序源码)——网络通讯系统

基于javaSocketsqlserver的网络通讯系统设计与实现&#xff08;毕业论文程序源码&#xff09; 大家好&#xff0c;今天给大家介绍基于javaSocketsqlserver的网络通讯系统设计与实现&#xff0c;文章末尾附有本毕业设计的论文和源码下载地址哦。 文章目录&#xff1a; 基于jav…

基于java+springmvc+mybatis+vue+mysql的少儿编程管理系统

项目介绍 在国家重视教育影响下&#xff0c;教育部门的密确配合下&#xff0c;对教育进行改革、多样性、质量等等的要求&#xff0c;使教育系统的管理和运营比过去十年前更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上少儿编程教育网站系统是一项十分…

820爆炸案(模拟案件)

文章目录模拟案件背景相关密码快压word邮箱BitLocker系统证书bestcrypt涉案图片模拟案件背景 8月20日18&#xff1a;00某市汽车站发生一起爆炸案件&#xff0c;经初步侦查&#xff0c;炸弹系通过手机远程引爆&#xff0c;办案人员经过综合研判分析&#xff0c;确定了引爆炸弹的…

并发编程之深入理解ReentrantLock和AQS原理

AQS&#xff08;AbstractQueuedSynchronizer&#xff09;在并发编程中占有很重要的地位&#xff0c;可能很多人在平时的开发中并没有看到过它的身影&#xff0c;但是当我们有看过concurrent包一些JDK并发编程的源码的时候&#xff0c;就会发现很多地方都使用了AQS&#xff0c;今…

Linux | 网络概念理解 | 对网络的初识

文章目录重新看待计算机体系结构软件分层的思想网络中的分层协议的理解局域网的理解MAC地址 && IP地址报头的作用端口号&#xff08;port&#xff09;重新看待计算机体系结构 计算机由硬件组成&#xff0c;而不同硬件之间要怎么通信&#xff0c;或者说要怎么进行数据的…

8年软件测试工程师感悟—我亲身经历的2022年软件质量工作

这两天和朋友谈到软件测试的发展&#xff0c;其实软件测试已经在不知不觉中发生了非常大的改变&#xff0c;前几年的软件测试行业还是一个风口&#xff0c;随着不断地转行人员以及毕业的大学生疯狂地涌入软件测试行业&#xff0c;目前软件测试行业“缺口”已经基本饱和。当然&a…