数据结构(Java):Map集合Set集合哈希表

目录

1、介绍

1.1 Map和Set

1.2 模型

2、Map集合

2.1 Map集合说明

2.2 Map.Entry<K,V>

2.3 Map常用方法

2.4 Map注意事项及实现类

 3、Set集合

3.1 Set集合说明

 3.2 Set常用方法

 3.3 Set注意事项及其实现类

4、TreeMap&TreeSet

4.1 集合类TreeMap(Key-Value模型)

4.1.1 底层结构

4.2 集合类TreeSet(纯Key模型)

4.2.1 底层结构

4.3 TreeMap与TreeSet之间的关系

4.3.1 再谈TreeSet之构造方法

 4.3.2 再谈TreeSet之add方法

4.4 TreeMap&TreeSet总结

 5、哈希表

5.1 概念

5.2 冲突-概念

5.2 冲突-避免

5.2.1 设计合理的哈希函数

 5.2.2 调节负载因子

5.3 冲突-解决

5.3.1 闭散列

 5.3.2 🌟开散列/哈希桶/开链法

5.4 HashMap&HashSet

5.4.1 hashCode与equals方法

5.5 性能分析

5.6 总结


1、介绍

1.1 Map和Set

Map和Set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。

Map和Set能够在查找时进行一些插入和删除的操作,即动态查找。

1.2 模型

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:

  1. 纯Key模型:数据仅包含关键字。
  2. Key-Value模型:数据除关键字外,还有关键字所对应的值。例如:统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>。

 Map集合是Key-Value模型;而Set集合是纯Key模型。


2、Map集合

2.1 Map集合说明

Map是一个接口类,该接口没有继承自Collection,是一个单独的接口,为Key-Value模型,存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复。  (K不可重复,V可重复)

2.2 Map.Entry<K,V>

Entry是Map接口的一个内部接口是用来存放<Key,Value>键值对映射关系的内部接口

内部接口Map.Entry<K,V>主要提供了<Key,Value>的获取,Value的设置以及Key的比较方式。

注意:Map.Entry<K,V>并没有提供设置Key的方法 !!!Key无法修改!!!

同样,实现Map接口的类也需要实现Entry接口:

这里以TreeMap的内部类Entry为例,Entry实现了的Map.Entry接口,而Entry就相当于我们之前所学二叉树的一个节点TreeNode。

TreeMap的底层是一个红黑树(下文详解),TreeMap的内部类Entry就相当于红黑树的一个节点,有key、value等属性。

2.3 Map常用方法

 演示如下:

若我们使用get方法来获取一个不存在的key的value值时,返回的value为null,

所以当value为Integer类型时,最好使用包装类Integer接收,若使用int基本类型接收会自动拆箱,可能抛出空指针异常:

需要注意的是keySet、values、entrySet方法:

  • keySet,可以将key值放在Set集合中,返回Set集合;
  • values,可以将value值放在Collection集合中,返回Collection集合;
  • entrySet,可以将key-value映射关系放在Set集合中,返回Set集合。

注意:Map系列集合是不能用迭代器实现遍历的,若想使用迭代器遍历,需要使用keySet、values、entrySet方法转换为Set集合,再使用迭代器遍历!!!

2.4 Map注意事项及实现类

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
  2.  Map中存放键值对的Key不能重复,value是可以重复的
  3. 在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。(TreeMap底层是一颗红黑树,涉及key之间的比较)
  4. HashMap的key和value都可以为空。
  5. Map中的Key可以全部分离出来,存储到Set中来进行访问(Key不能重复)。
  6. Map中的value可以全部分离出来,存储在Collection的集合中,若存储在其子集中则强转为Collection(value可以有重复)。
  7. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。 
  8. TreeMap和HashMap是实现Map的集合类

 TreeMap与HashMap:

下文细讲TreeMap与HashMap。 


 3、Set集合

3.1 Set集合说明

Set集合是纯Key模型,也就是说,Set只存储了Key。

Set集合中的Key值也不能重复存在,能够达到天然去重的效果。

Set与Map主要的不同有两点:

  1. Set是继承自Collection的接口类。
  2. Set是纯Key模型,只存储了Key。

 3.2 Set常用方法

因为Set是是继承自Collection的接口类,所以其方法很多与我们之前学的Collection接口下的类是相同的,这里不再赘述。

需要注意的是:

  • 如果将其他集合的元素添加到Set集合中,可以到达天然去重的效果;
  • Set集合可以使用迭代器遍历;

 3.3 Set注意事项及其实现类

  1. Set是继承自Collection的一个接口类。
  2. Set中只存储了key,并且要求key一定要唯一
  3. TreeSet的底层是使用TreeMap来实现的,其使用Key与Object的一个默认对象作为键值对插入到Map中。
  4. Set最大的功能就是对集合中的元素进行去重
  5. 实现Set接口的常用类有TreeSet和HashSet(下文细讲),还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
  6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
  7. TreeSet中不能插入null的key(底层为搜索二叉树,涉及key的比较),HashSet可以。

 TreeSet与HashSet:


4、TreeMap&TreeSet

 为什么要将TreeMap和TreeSet放到一起说呢?因为这两者间是有关系的。

4.1 集合类TreeMap(Key-Value模型)

4.1.1 底层结构

TreeMap是Map集合下的一个集合类,底层是一颗红黑树,红黑树是一颗二叉搜索树,所以其Key值必须具备可比较功能。

也就是说,如果其Key是自定义类型,则这个自定义类必须实现Comparable接口或者给TreeMap的构造方法提供比较器。

 因为TreeMap底层为红黑树,故其插入删除查找元素的时间复杂度为O(logN)

4.2 集合类TreeSet(纯Key模型)

4.2.1 底层结构

TreeSet是Set集合下的一个集合类,底层也是一颗红黑树,红黑树是一颗二叉搜索树,所以其Key值也必须具备可比较功能。

故,TreeSet插入删除查找元素的时间复杂度也为O(logN)

同样,如果其Key是自定义类型,则这个自定义类必须实现Comparable接口或者给TreeSet的构造方法提供比较器。

4.3 TreeMap与TreeSet之间的关系

虽然TreeMap是Map下的集合,TreeSet是Set下的集合,

但是细心的小伙伴已经发现,TreeMap和TreeSet的底层结构不仅都是红黑树,而且他们的Key值都不能重复出现,那么他们之间是不是有什么关系呢?

没错,其实TreeSet的底层是使用TreeMap来实现的。

可是为什么呢?接下来,让我们从TreeSet的源码中找到原因。

4.3.1 再谈TreeSet之构造方法

我们可以看到,当我们调用TreeSet的无参构造方法创建TreeSet对象时,其实是创建了一个TreeMap对象并传给了带一个参数的构造方法,并用NavigableMap类型的成员变量m将这个TreeMap对象引用起来。

NavigableMap是什么呢,我们点过去后发现是一个接口,并且拓展了SortedMap接口,而我们发现SortedMap接口拓展了Map接口

我们再观察TreeMap,发现TreeMap实现了NavigableMap接口。

所以,其实TreeMap有着下图的实现结构:

也就是说NavigableMap可以用来接收TreeMap对象实现向上转型,同时也说明,当我们实例化一个TreeSet对象时,实际上创建的是一个TreeMap对象。

也就是说,TreeSet的底层其实就是TreeMap!!!

 4.3.2 再谈TreeSet之add方法

因为TreeSet的底层是TreeMap,所以添加元素add时,实际上调用的是TreeMap的put方法,而其value值是PRESENT,而PRESENT永远都是一个Object对象。 

 

所以,不管add的Key是什么东西,其value值永远都是一个Object对象。

4.4 TreeMap&TreeSet总结

  •  TreeMap底层是一颗红黑树,二叉搜索树。
  • TreeSet的底层是TreeMap,所以TreeSet也是一颗红黑树,二叉搜索树。
  • TreeSet的底层是TreeMap,所以其Key不可重复,具有Map的去重功能。

 5、哈希表

5.1 概念

在顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码(Key)的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度即O(logN),搜索的效率取决于搜索过程中 元素的比较次数。

而哈希表可以通过哈希函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,不经过任何比较,一次直接从表中得到要搜索的元素。

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(或者称散列表)。

插入元素:

  • 根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放

查找元素:

  • 对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若 关键码相等,则搜索成功 

哈希表 插入/删除/查找操作的时间复杂度为 O(1)。

5.2 冲突-概念

不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

 把具有不同关键码而具有相同哈希地址的数据元素称为"同义词"。

 由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,

这就导致一个问题:冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率

5.2 冲突-避免

避免冲突有两个办法,一个是设计合理的哈希函数,一个是调节负载因子。

5.2.1 设计合理的哈希函数

哈希函数设计原则:

  1. 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
  2. 哈希函数计算出来的地址能均匀分布在整个空间中
  3. 哈希函数应该比较简单

常见哈希函数:

 注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

在实际应用中,其实也轮不到我们来设计哈希函数...hhh😂

 5.2.2 调节负载因子

负载因子的计算公式为:负载因子 = 已有键值对数量 / 散列表容量。

它表示在散列表中,当插入一个新的键值对时,可以允许的最大填充程度。

负载因子越大,散列表的填充程度越高,冲突的发生率越高。相反,负载因子越小,散列表的填充程度越低,插入和查找操作的性能可能会更好,冲突的发生率越低,但空间利用率会降低。

因为我们不能降低元素填入个数,所以我们只能扩容哈希表来降低冲突率。

当元素个数和散列表容量的比值接近或等于负载因子时,就要对哈希表进行扩容操作。

换句话说,哈希表就是以空间换取时间的一种数据结构。

Java中,定义的负载因子大小为0.75。

5.3 冲突-解决

我们知道,冲突的发生是必然的,那么,当冲突发生后,我们该如何做呢?

解决哈希冲突两种常见的方法是:闭散列和开散列。

5.3.1 闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的"下一个"空位置中去。

找"下一个”"空位置,有两种方法: 

  • 线性探测法:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。(线性探测的缺陷是产生冲突的数据堆积在一块)
  • 二次探测法:采用移动数值的平方次来找空位置。例如:如果键15的哈希值对应的空间已经被占用,算法可能会尝试使用平方数序列(如1^2, -1^2, 2^2, -2^2, ...)来计算新的哈希值,直到找到一个空位置为止。这种方法有助于避免哈希表中数据的聚集,提高哈希表的性能和数据的均匀分布。‌

 5.3.2 🌟开散列/哈希桶/开链法

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

拉链法解决冲突的特点:

  1. 将所有关键字为同义词的结点链接在同一个单链表中。
  2. 拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短。

即 数组+链表模式:

也就是说,开散列中每个桶中放的都是发生哈希冲突的元素。

HashMap采用的就是开散列法解决冲突。

但是当冲突严重时,链表的长度就会过于长,这样会影响搜索性能,

在Java中,当 数组长度超过64 && 链表长度超过8 ,链表就会转化为红黑树。

5.4 HashMap&HashSet

5.4.1 hashCode与equals方法

  • hashCode方法的作用是生成哈希值,通过哈希值计算出key在数组中的位置下标
  • equals方法的作用是判断,两个key的内容是否相等

例如:在使用put或者getValue方法时,通过hashCode找到元素的位置下标后,还需要使用equals方法一个一个的和链表中的元素比较,找到该元素在链表中的具体位置,若equals为true,说明找到了该元素。

故:当哈希表中的key为自定义类时,一定要重写equals和hashCode方法!!! 

注意:

  1. 若两个key通过equals比较结果为true,说明两个key的内容相同,说明通过hashCode得到的哈希值一定是相等的;
  2. 但是,若两个key 通过hashCode得到的哈希值相等,那么只能说明这两个key在数组中的位置是相同的,不能说明两个key的内容相同,也就是说equals可能为true也可能为flase。

总结:如果equals为true,那么hashCode出的哈希值一定相等; 如果hashCode出的哈希值相等,那么equals不一定为true!!!

5.5 性能分析

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

5.6 总结

  1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
  2. HashMap 和 HashSet 中使用的是 哈希桶/开散列 方式解决冲突的
  3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
  4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作 HashMap 的key值或者 HashSet 的key值,必须重写 hashCode 和 equals 方法。
  5. equals 相等的对象,hashCode 一定是一致的。
  6. hashCode一致,equals不一定相等。
  7. HashMap和HashSet不涉及元素之间的比较,所以不用像TreeMap或TreeSet具备可比较功能。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式中什么是三次握手

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c;点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 在网络数据传输中&#xf…

pytorch3d的安装

在这个网址中&#xff0c;下载对应的pytorch3d安装包 https://anaconda.org/pytorch3d/pytorch3d/files下载完成后使用下面命令进行安装 conda install ./pytorch3d-0.7.7-py39_cu118_pyt201.tar.bz2

可见性::

目录 定义&#xff1a; 解决方法&#xff1a; ①使用synchronized实现缓存和内存的同步 修改一&#xff1a; 加入语句&#xff1a; 代码&#xff1a; 修改2&#xff1a; 在代码块中加入&#xff1a; 代码&#xff1a; 执行结果&#xff1a; 原因&#xff1a; ②使用…

Linux--Socket 编程 UDP(简单的回显服务器和客户端代码)

目录 0.上篇文章 1.V1 版本 - echo server 1.1认识接口 1.2实现 V1 版本 - echo server&#xff08;细节&#xff09; 1.3添加的日志系统&#xff08;代码&#xff09; 1.4 解析网络地址 1.5 禁止拷贝逻辑&#xff08;基类&#xff09; 1.6 服务端逻辑 &#xff08;代码&…

【C/C++】printf和cout的区别

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

解析资源库架构模式

无论应用程序的设计方法和实现技术如何发展&#xff0c;数据访问仍然是任何系统都需要考虑的基础技术问题。针对数据访问过程&#xff0c;我们可以理解为任何一个系统都应该存在这样一个起点&#xff0c;我们可以从这个起点遍历到具体的数据。换句话说&#xff0c;系统中应该存…

Python爬虫掌握-----4实战(爬取视频)

我们使用爬虫时难免会遇到爬取视频的情况&#xff0c;其实爬取图片视频&#xff0c;内容都是一样的。这里以b站视频为例。 一、开始 1.找到url&#xff0c;请求url 防盗链&#xff0c;需要写在UA伪装中 正常的三步&#xff1a; 1.url 2.requests请求 3.UA伪装 import req…

2024最新网络安全自学路线,内容涵盖3-5年技能提升

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

做一个能和你互动玩耍的智能机器人之二

智能机器人硬件的一些注意事项和知识和初学者的误区。 一。不要担心不会焊接&#xff0c;我也是购买后才发现&#xff0c;现在的很多板子和元器件可以无焊接使用&#xff0c;比如借助杜邦线和面包板&#xff0c;可以很方便连接。 二。arduino有很多种&#xff0c;一般用的是n…

【Java算法专场】二分查找(上)

目录 前言 什么是二分查找&#xff1f; 二段性 ​​​​​​​​​​​​​​​​​​​​​二分查找 算法分析 算法步骤 算法代码 算法示例 模板 在排序数组中查找元素的第一个和最后一个位置 算法分析 算法步骤 算法代码 算法示例 搜索插入位置 算法分析 算法步…

IEC104转MQTT网关支持将IEC104数据转换为华为云平台可识别的格式

随着智能电网和物联网技术的深度融合&#xff0c;传统电力系统中的IEC104协议设备正逐步向更加开放、智能的物联网体系转型。华为云作为全球领先的云计算和AI服务提供商&#xff0c;其物联网平台为IEC104设备的接入与数据处理提供了强大的支撑。本文将探讨IEC104转MQTT网关在MQ…

KETTLE运行出现乱码和无法执行问题及解决方案

一、乱码问题 &#xff08;1&#xff09;出现乱码&#xff0c;在数据库连接里面的选项里面加入&#xff1a;characterEncodingutf8和tinyInt1isBitfalse &#xff08;2&#xff09;取消简易转换&#xff0c;点开表输入&#xff0c;取消”允许简易转换”选项&a…

vue3.0学习笔记(一)——vue3简介与vite脚手架的使用

1. 为什么学vue3 Vue3现状&#xff1a; vue-next 2020年09月18日&#xff0c;正式发布vue3.0版本。但是由于刚发布周边生态不支持&#xff0c;大多数开发者处于观望。现在主流组件库都已经发布了支持vue3.0的版本&#xff0c;其他生态也在不断地完善中&#xff0c;这是趋势。…

梯度下降算法在逻辑回归中的应用

逻辑回归简介 sigmoid函数&#xff1a; g ( z ) 1 1 e − z g(z) \frac{1}{1e^{-z}} g(z)1e−z1​ 逻辑回归假设函数&#xff1a; y ^ h θ ( x ) g ( θ T x ) 1 1 e − θ T x \hat{y} h_{\theta}(x) g(\theta^Tx) \frac{1}{1e^{-\theta^Tx}} y^​hθ​(x)g(θTx)…

我的世界!

每位冒险家在《我的世界》中的出生点都各不相同&#xff0c; 有的出生在桦木森林&#xff0c;有的出生在草原&#xff0c; 还有的出生在临近海洋的沙滩。 这些环境叫做生物群系&#xff0c;也常被称为生态系统。 在《我的世界》中的不同生物群系具有不同的地域特色—— 不…

Redis的五种数据类型与命令

目录 引言 一 Redis的特性 二 Redis的安装 三 Redis的优点 四 Redis的五种数据类型与命令 五 Redis的配置文件 引言 Redis是什么&#xff1f; Remote Dictionary Service(远程字典服务器) Redis 是一个开源的(BSD许可)的&#xff0c;C语言编写的&#xff0c;高性能的数…

羊大师:夏夜贪凉,但为啥肚子还要‘保暖计划’?

在这个夏夜&#xff0c;当空调与风扇齐飞&#xff0c;冰镇西瓜与凉面共舞之时&#xff0c;你是否也曾有过这样的疑惑&#xff1a;明明热得汗流浃背&#xff0c;为啥老一辈总念叨着“睡觉再热也要给肚子盖被子”&#xff1f;这背后&#xff0c;藏着的可不仅仅是老一辈的固执&…

centos7手动编译安装redis-6.2.1.tar.gz

本章教程,主要通过手动编译安装的方式,进行安装redis-6.2.1版本,如果需要安装其它版本的,可以在这里找到对应版本进行下载,安装步骤基本上差不多。 下载地址:https://download.redis.io/releases/ 一、下载安装包 wget https://download.redis.io/releases/redis-6.2.1.…

SSM学习9:SpringBoot简介、创建项目、配置文件、多环节配置

简介 SpringBoot式用来简化Spring应用的初始搭建以及开发过程的一个框架 项目搭建 File -> New -> Project 选中pom.xml文件&#xff0c;设置为maven项目 项目启动成功 可以访问BasicController中的路径 配置文件 在resources目录下 application.properties 默…

powershell自定义命令别名

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、查看命令别名二、常见的别名三、自定义别名1.GUI编辑2.命令行编辑 总结 前言 有时候在windows上使用powershell时候常常苦于别名问题&#xff0c;像我这样…