【HashMap和HashSetyi以及散列表的拉链法,线性探测法详解】

在这里插入图片描述

🌈个人主页:SKY-30
个人推荐:基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解
学好数据结构,刷题刻不容缓:点击一起刷题
🌙心灵鸡汤总有人要赢,为什么不能是我呢
在这里插入图片描述

📢📢📢Map 和Set 概念

map和set是一种用来搜索的数据结构,其搜索效率与具体实例化的子类有关,之前我们搜索的方法有:

  • 直接遍历:利用for循环,暴力求解,事件复杂度为O(N)
  • 二分查找:时间复杂度:O(logN),搜索之前要求序列是有序的

但是如果我们对于存储的数据,经常要执行修改和删除的操作,那么上述的两种操作就非常不适合了,我们这里就引出两个新的数据结构-map和set。

📢📢📢模式

对于map来说,它的存储形式是形如K-(key),V-(value),这种类似于键值对的这种形式,进行存储,而set,则是只有key这一种形式。

📢📢📢Map

在这里插入图片描述
Map是一个接口,没有继承collection,该类的存储形式是<K,V>并且这里的K是唯一的,不能重复。

📢📢📢Map.Entry<K,V>

Map.Entry<K,V>是Map内部用来存放<key,value>键值对的映射关系的一个内部类,该类提供了<Key,value>获取,value的设置,即Key的比较形式

方法解释
K getKey()返回 entry中的key
V getValue返回entry中的value
V setValue将键值对中的value替换为指定的value

📢📢📢Map的一些常用方法

在这里插入图片描述

📢📢📢Map的一些注意事项

  • Map是一个接口,不能直接实例化对象,如果要实例化,我们只能实例实现其接口的类,TreeMap和HashMap。
  • Map中Key的元素是唯一的,Value可以重复的
  • 在Treemap中插入元素时,Key不能为空,否则会报错,value却可以为空,需要注意在HashMap中是可以插入为空的
  • Map中的Key是不能修改的,要想修改的话,只能删除对应的数据,然后重新插入,但是Value是可以重新修改的。
  • Treemap和HashMap的区别,这个后面说
import java.util.TreeMap;
import java.util.Map;
public static void TestMap(){
Map<String, String> m = new TreeMap<>();
// put(key, value):插入key-value的键值对
// 如果key不存在,会将key-value的键值对插入到map中,返回null
m.put("林冲", "豹子头");
m.put("鲁智深", "花和尚");
m.put("武松", "行者");
m.put("宋江", "及时雨");
String str = m.put("李逵", "黑旋风");
System.out.println(m.size());
System.out.println(m);
// put(key,value): 注意key不能为空,但是value可以为空
// key如果为空,会抛出空指针异常
//m.put(null, "花名");
str = m.put("无名", null);
System.out.println(m.size());
// put(key, value):
// 如果key存在,会使用value替换原来key所对应的value,返回旧value
str = m.put("李逵", "铁牛");
// get(key): 返回key所对应的value
// 如果key存在,返回key所对应的value
// 如果key不存在,返回null
System.out.println(m.get("鲁智深"));
System.out.println(m.get("史进"));
//GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
System.out.println(m.getOrDefault("李逵", "铁牛"));
System.out.println(m.getOrDefault("史进", "九纹龙"));
System.out.println(m.size());
//containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
// 按照红黑树的性质来进行查找
// 找到返回true,否则返回false
System.out.println(m.containsKey("林冲"));
System.out.println(m.containsKey("史进"));
// containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)
// 找到返回true,否则返回false
System.out.println(m.containsValue("豹子头"));
System.out.println(m.containsValue("九纹龙"));
// 打印所有的keyfor(String s : m.keySet()){
System.out.print(s + " ");
}
System.out.println();
// 打印所有的value
// values()是将map中的value放在collect的一个集合中返回的
for(String s : m.values()){
System.out.print(s + " ");
}
System.out.println();
// 打印所有的键值对
// entrySet(): 将Map中的键值对放在Set中返回了
for(Map.Entry<String, String> entry : m.entrySet()){
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
System.out.println();
}
// keySet是将map中的key防止在Set中返回的}

在这里插入图片描述

📢📢📢Set-详解

Set与Map很大的一个不同就是Set继承了collection而map并没有。

⚡⚡⚡一些常见的方法

在这里插入图片描述

⚡⚡⚡针对Set的一些注意事项

  • Set是继承了collection的一个接口类
  • Set中只存储了Key,并且要求他是唯一的,不能重复
  • Set的最大的一个特点就是对数据去重。
  • 实现Set接口的两个常见的类是HashSet和Treemap
  • TreeSet中的key不能插入null,HashSet则可以插入null
  • 在这里插入代码片
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Set;
public static void TestSet(){
Set<String> s = new TreeSet<>();
// add(key): 如果key不存在,则插入,返回ture
// 如果key存在,返回false
boolean isIn = s.add("apple");
s.add("orange");
s.add("peach");
s.add("banana");
System.out.println(s.size());
System.out.println(s);boolean isIn= s.add("apple");
// add(key): key如果是空,抛出空指针异常
//s.add(null);
// contains(key): 如果key存在,返回true,否则返回false
System.out.println(s.contains("apple"));
System.out.println(s.contains("watermelen"));
// remove(key): key存在,删除成功返回true
// key不存在,删除失败返回false
// key为空,抛出空指针异常
s.remove("apple");
System.out.println(s);
s.remove("watermelen");
System.out.println(s);
Iterator<String> it = s.iterator();
while(it.hasNext()){
System.out.print(it.next() + " ");
}
System.out.println();
}

在这里插入图片描述

📢📢📢哈希表

不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

  • 当向该结构中:
    插入元素
    根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。
    搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功!!!

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

⚡⚡⚡冲突

不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
如何解决哈希碰撞呢,这里几种不同的方法给大家具体说明一下。

⚡⚡⚡解决哈希冲突的及汇总方法

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。 哈希函数设计原则:哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间哈希函数计算出来的地址能均匀分布在整个空间中哈希函数应该比较简单

常见的哈希函数:
除留余数法:
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数: Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

除此之外,我们还发现哈希冲突与负载因子有关,所以如何利用负载因子降低冲突率呢,首先负载因子的计算公式是:填入表中的数据的个数/散列表的长度
在这里插入图片描述
由于我们无法改变插入表中的数据,所以我们只能改变表的长度了。

⚡⚡⚡冲突-解决-闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以
把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置呢?
这里我们介绍一种比较常见的方法

线性探测
比如,现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

在这里插入图片描述

但是线性探测这种方法,存在一定的问题,比如他会导致数据全部聚集在同一块区域,导致我们后面插入数据的时候,可能要讲过多次的查找操作,所以,我们为了解决这个问题,发明了另一种探测方法。

二次探测
相比与线性探测,二次探测通过制定的函数,可以将数据均匀的插入到数组当中,避免数据扎堆的情况。
找下一个空位置的方法为: H= (H0 +i*i )% m, 其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,
m是表的大小。

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不
会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情
况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。
因此:比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

⚡⚡⚡冲突-解决-开散列/哈希桶(重点掌握)

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

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

// key-value 模型
public class HashBucket {
private static class Node {
private int key;
private int value;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
比特就业课
}
private Node[] array;
private int size; // 当前的数据个数
private static final double LOAD_FACTOR = 0.75;
public int put(int key, int value) {
int index = key % array.length;
// 在链表中查找 key 所在的结点
// 如果找到了,更新
// 所有结点都不是 key,插入一个新的结点
for (Node cur = array[index]; cur != null; cur = cur.next) {
if (key == cur.key) {
int oldValue = cur.value;
cur.value = value;
return oldValue;
}
}
Node node = new Node(key, value);
node.next = array[index];
array[index] = node;
size++;
if (loadFactor() >= LOAD_FACTOR) {
resize();
}
return -1;
}
private void resize() {
Node[] newArray = new Node[array.length * 2];
for (int i = 0; i < array.length; i++) {
Node next;
for (Node cur = array[i]; cur != null; cur = next) {
next = cur.next;
int index = cur.key % newArray.length;
cur.next = newArray[index];
newArray[index] = cur;
}
}
array = newArray;
}
private double loadFactor() {
return size * 1.0 / array.length;
}
public HashBucket() {
array = new Node[8];
size = 0;
}
public int get(int key) {
int index = key % array.length;
Node head = array[index];
for (Node cur = head; cur != null; cur = cur.next) {
if (key == cur.key) {
return cur.value;
}
}
return -1;
}
}

⚡⚡⚡关于HashMap和HashSet的注意事项

  1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
  2. java 中使用的是哈希桶方式解决冲突的
  3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
  4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方
    法,而且要做到 equals 相等的对象,hashCode 一定是一致的。

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

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

相关文章

昇思25天学习打卡营第2天 | 张量Tensor

张量Tensor 张量&#xff08;Tensor&#xff09;基础 张量是MindSpore中的基本数据结构的一种&#xff0c;类似于NumPy中数组和矩阵非常相似。它具有以下重要属性&#xff1a; 形状&#xff08;shape&#xff09;和数据类型&#xff08;dtype&#xff09;&#xff1a;每个张量…

射频技术在未来通信中的应用

低功耗广域网&#xff08;LPWAN&#xff09;技术 物联网的广泛应用需要低功耗、广覆盖的无线通信技术。低功耗广域网&#xff08;LPWAN&#xff09;技术如LoRa、NB-IoT和Sigfox等应运而生。这些技术在低功耗设计、远距离传输和大规模设备连接方面具有显著优势&#xff0c;使其…

Docker 常见命令快速查阅

基础命令 1. 帮助命令 docker versiondocker infodocker --help2. 镜像相关命令 列出主机的所有镜像 docker images [OPTIONS] # OPTIONS 说明&#xff1a;-a :列出本地所有的镜像&#xff08;含中间映像层&#xff09;-q :只显示镜像ID--digests :显示镜像的摘要信息--no-…

【Java基础】 网络编程

在数字化时代&#xff0c;网络编程已成为软件开发中的重要组成部分。无论是构建分布式系统、实现实时通讯还是进行Web开发&#xff0c;都需要深入了解网络编程的基础知识和技术。这种技能不仅在IT领域至关重要&#xff0c;还在非计算机行业的岗位中发挥着越来越重要的作用。Jav…

如何找到合适的Python第三方库?

找合适的Python库其实很简单&#xff0c;按照以下三步法&#xff0c;你能找到90%的Python库。 1、百度谷歌搜索 明确自己的需求&#xff0c;用Python来干什么&#xff0c;力求简短明了。比如定位“数据分析”&#xff0c;然后去搜索关键词【Python数据分析第三方库】&#xf…

基于 JWT 进行身份验证

一、JWT 介绍 JWT 本质上就是一组字串&#xff0c;通过&#xff08;.&#xff09;切分成三个为 Base64 编码的部分&#xff1a; Header : 描述 JWT 的元数据&#xff0c;定义了生成签名的算法以及 Token 的类型。Payload : 用来存放实际需要传递的数据Signature&#xff08;签…

考研数学|张宇和武忠祥,强化能不能同时跟?

可以说你跟武老师学明白了&#xff0c;120完全没问题&#xff01;如果追求更高&#xff0c;宇哥的怀抱也想你敞开&#xff01; 学长我21年一战数学83&#xff0c;总分没过线&#xff0c;22年二战143&#xff0c;逆袭上岸211&#xff01;市面上的老师我基本都听过&#xff0c;最…

微深节能 料场堆取料无人操作系统 格雷母线

格雷母线高精度位移检测系统在料场堆取料中的应用是一个重要的技术进步&#xff0c;它极大地提升了堆取料作业的自动化和精确性。 一、格雷母线定位系统概述 格雷母线高精度位移测量系统&#xff0c;包括格雷母线、天线箱、地址解码器、地址编码器四个部分组成。 格雷母线类似一…

大数据面试题之Hive(3)

目录 Hive的函数:UDF、UDAF、UDTF的区别? UDF是怎么在Hive里执行的 row_number&#xff0c;rank&#xff0c;dense_rank的区别 Hive count(distinct)有几个reduce&#xff0c;海量数据会有什么问题 HQL&#xff1a;行转列、列转行 一条HQL从代码到执行的过程 了解Hive S…

深入探索Kylin的Cube构建:数据魔方的构建之旅

深入探索Kylin的Cube构建&#xff1a;数据魔方的构建之旅 引言 Apache Kylin是一个开源的分布式分析引擎&#xff0c;提供Hadoop和Spark之上的高性能数据立方体&#xff08;Cube&#xff09;技术。Kylin的Cube构建过程是其核心功能之一&#xff0c;它允许用户定义和构建多维数…

手动指定ubuntu环境下程序打包指定目录

Ubuntu在打包过程请自行检索其它文章&#xff1b; 在打包时候默认不能打包一些静态资源的&#xff0c;比如以配置文件为例config目录 当程序打包安装后&#xff0c;安装目录中没有config目录&#xff0c;这时候就需要我们手动创建config目录及目录中对应的配置文件等内容&…

Java养老护理助浴陪诊小程序APP源码

&#x1f496;护理助浴陪诊小程序&#x1f496; 一、引言&#xff1a;养老新趋势&#x1f331; 在快节奏的现代生活中&#xff0c;养老问题逐渐成为了社会关注的焦点。如何为老年人提供便捷、贴心的服务&#xff0c;让他们晚年生活更加安心、舒适&#xff0c;是我们每个人都需…

持续总结中!2024年面试必问的操作系统面试题(三)

上一篇地址&#xff1a;持续总结中&#xff01;2024年面试必问的操作系统面试题&#xff08;二&#xff09;-CSDN博客 五、什么是分页和分段&#xff1f;它们之间有什么区别&#xff1f; 分页和分段是操作系统中用于内存管理的两种不同技术&#xff0c;它们都旨在允许操作系统…

v-model中.lazy、.number、.trim

在Vue.js中&#xff0c;v-model 是一个非常核心的指令&#xff0c;它主要用于在表单输入和应用状态之间建立双向绑定。 Vue.js 提供了一些修饰符&#xff08;modifiers&#xff09;&#xff0c;这些修饰符可以用来指定不同的行为。.lazy、.number 和 .trim 就是这样的修饰符。 …

篮球联盟管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;球员管理&#xff0c;用户管理&#xff0c;球队管理&#xff0c;论坛管理&#xff0c;篮球资讯管理&#xff0c;基础数据管理 前台账户功能包括&#xff1a;系统首页&#xff0…

Transformer常见面试题

目录 1.Transformer为何使用多头注意力机制&#xff1f;&#xff08;为什么不使用一个头&#xff09; 2.Transformer为什么Q和K使用不同的权重矩阵生成&#xff0c;为何不能使用同一个值进行自身的点乘&#xff1f; &#xff08;注意和第一个问题的区别&#xff09; 3.Transf…

位运算符计算规则及使用场景

位运算符包括按位与&、按位异或^、按位或|、按位取反~、左移<<、右移>> 1.&&#xff08;按位与&#xff09;1&#xff09;限定数值范围2&#xff09;权限检测3&#xff09;掩码操作 2.^&#xff08;按位异或&#xff09;1&#xff09;两值交换2&#xff09…

git提交新仓库代码,提示无权限,但用户名已修改

目录 1 用户名无权限 2 删除用户凭据 2.1 打开控制面板 2.2 找到“凭据管理器” 2.3 删除git历史 3 npm工具库添加git仓库指引 1 用户名无权限 之前因为时间的原因&#xff0c;js-tool-big-box工具库没有提交到github上去&#xff0c;今天想着往上提交一下&#xff0c;但…

Linux系统中根下的目录结构介绍

一、Linux的路径分隔符 Linux系统中使用正斜杠(/)作为路径分隔符&#xff1b;每个目录的后面都默认带有一个正斜杠&#xff08;如&#xff1a;需要进入opt目录可以分别使用【cd /opt】或【cd /opt/】&#xff09; 二、Linux根目录下各个目录结构介绍 红色标识的文件夹为Linux的…

“开源AI”到底是什么意思

开源与专有软件之间的斗争早已为人所熟知。然而&#xff0c;长期以来弥漫在软件圈的紧张关系已经渗透到了人工智能领域&#xff0c;部分原因在于没有人能在AI背景下就“开源”的真正含义达成一致。 相关阅读&#xff1a;GPT-4o通过整合文本、音频和视觉实现人性化的AI交互&…