【数据结构】Java的HashMap 和 HashSet 大全笔记,写算法用到的时候翻一下,百度都省了!(实践篇)
本篇会加入个人的所谓鱼式疯言
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
-
HashSet
-
HashMap
前言
在探索Java集合框架的丰富世界时,我们不可避免地会遇到两个至关重要的成员:HashMap和HashSet。这两个组件虽然在功能上有着明显的交集,但它们在内部实现、性能表现以及使用场景上却有着显著的差异。本文将深入探讨HashMap与HashSet的内部机制,比较它们的性能特点,并指导读者如何在实际开发中做出恰当的选择
一. HashSet
1. set 的初识
对于
set
来说, 继承自Collection
的类, 可以由SortedSet
和TreeSet
和HashSet
来实现。
对于
set 本身
来说, 只 存储 key 的值, 并不存values
, 并且这个 key是唯一的, 且 不可修改的 。
在本篇文章中将重点讲解 HashSet
来实现 Set 类 。
鱼式疯言
set
本身的含义有: 设置, 集合 。
在 Java集合框架 中,
set
的含义就表示 集合 。
集合中的关键码
(key)
就有 两大特性 : 1.无序性
2.唯一性
2. set 的常见方法使用
如上图, 小编介绍几种常见的方法, 足够我们平常 面试和刷算法题 中使用了
如果还想了解的小伙伴可以 参考下面的官方文档 哦 💕 💕 💕 💕
set 官方文档介绍
3. 代码演示
class Test1{public static void main(String[] args) {Set<Integer> set = new HashSet<>();set.add(1);set.add(2);set.add(3);set.add(4);set.add(5);// 使用迭代器 接收Iterator<Integer> iterator = set.iterator();// 输出迭代器while(iterator.hasNext()) {System.out.println(iterator.next());}// 判断 4 是否存在if(set.contains(4)) {System.out.println("删除前"+ 4 + "存在!");} else {System.out.println("删除后"+ 4 + "不存在!");}// 得到key 的个数System.out.println(set.size());set.remove(4);if(set.contains(4)) {System.out.println("删除后" + 4 + "存在!");} else {System.out.println("删除后" + 4 + "不存在!");}}}
-
add()
添加了 key 值 , 如果set
中存在则 添加失败 -
contains()
检查 是否存在该key
-
remove()
删除
某个key -
size()
得到key 的个数 -
iterator 迭代器
, 用来得到全部的key
的一个 整体的迭代器
鱼式疯言
补充说明
- 对于
set
的用法主要是用于判断是否 有重复元素 , 并且进行去重操作
。 并且用HashSet
实现, 时间复杂度 可以达到O(1)
。
- 对于
HashSet 实现的 set 接口
, 是可以进行 插入 null 的 。
二. HashMap
1. Map 的初识
对于
Map
而已, 该接口 不继承Collection 接口 , 可有 HashMap 或 TreeMap 实现,
并且
Map<K, V>
中存储的是 一对K和 V 的键值对。
其中 K 是 关键码(key) 用来 作为标识 , 是用来转化为
哈希地址
的 索引标识, 是 唯一的 , 不可更改 , 不能重复 。
而 V 是 值 (value) , 用来存储具体需要 存储的数据 。
上面的解释, 可能小伙伴们还不是很能理解
不妨回忆下我们上篇文章的 哈希表的建立
其中数组的下标就可以作为
索引值
, 而key 就可以通过 某种哈希函数 来转化为 数组的下标 。
而
value
就可以刚好对应数组下标位置
所 对应的值 。
鱼式疯言
小知识 :
对于 HashMap 实现的Map 来说, 键值对都是可以置为 null
的。
2. Map.Entry<K, V>
Map.Entry<K,V>
是一种专门 存放键值对的一种类型 , 并且是 Map 的静态方法
, 可以 不需要对象调用 。
其实有下面三种方法 ,小编在这里就展示 前两个 。
下面我们来看看其演示过程
public static void main(String[] args) {// entryMap.Entry<String , String> entry = Map.entry("s","t");System.out.println(entry.getValue());System.out.println(entry.getKey());}
如上图
其中
s
就代表 key 的关键码,可以用getValue
获取到
而
t
就表示 value 的关键码 , 可以用getKey
来 获取到。
这个类型 其实用的不多, 小伙伴们只需要了解一下即可。
鱼式疯言
补充说明 :
虽然小编上面说明有提供了 修改Value 的方法, 但是并 没有提供 修改
Key
的方法。
所以对于key
来说, 是 不可修改的 。
3. Map 的常见方法使用
对于上述的方法来说, get ()
, getOrDefault()
, put()
, remove()
, keySet()
, values()
等…
以上这些方法, 小编都会一一演示。
4. 代码演示
import java.util.*;
public class Test {public static void main(String[] args) {// 实例化一个 map 对象Map<String, String> map = new HashMap<>();map.put("小白", "dog");map.put("小黑", "cat");map.put("小头", "sleep");map.put(null, null);System.out.println("正在遍历 value 值: ");// 使用 values 演示得到的所有的value 的结果Collection<String> strings = map.values();for(String strings1 : strings) {System.out.print(strings1 + " ");}System.out.println();// 使用 keyset 演示所以得到的 key 的结果System.out.println("正在遍历 key 值: ");Set<String> strings1 = map.keySet();for (String str : strings1) {System.out.print(str+ " ");}System.out.println();// 判断map 是否存在 某个特定的 value 和 keyif(map.containsKey("小白")) {System.out.println("存在小白!");if (map.containsValue("dog")) {System.out.println("存在小白, 并且小白是条狗");} else {System.out.println("存在小白, 但是小白不是条狗!");}}// 获取小黄的value , 如果没有就会返回 defaultValue 的特定的valueString ret = map.getOrDefault("小黄", "不存在小黄, 但这里会返回一个小蓝");System.out.println(ret);}}
-
通过
get()
其中参数列表中填入的 key 的值, 然后 调用 get() 之后 , 就会得到该key
所对应的 value 值 。 -
getOrvalue()
如果有key
就返回 对应的value 值 , 如果没有就 返回后面那个参数的结果 。 -
put()
就相等于 插入元素 , 不仅要插入 key , 也要 插入key 所对应的value 。 从而得到 两者相关联 , 一样对应的关系 的作用。 -
remove() , 是直接删除 key , 并且 一旦删除 key , 那么 对应的value 也会被删除。
-
keyset() , 这个方法的作用就在于 获取map 中所有的key 值 , 并且用前面讲解过的
Set<Key>
来接收。 -
values()
这个方法的含义就在于 获取map 中所有的 value值 , 返回值是一个Collection<value>
来接收。 -
containsKey()
, 用于判断指定的key 是否存在
-
containsValue()
, 用于判断指定的 value 是否存在 。
鱼式疯言
补充说明:
-
对于map 来说, key 是不可重复的, 但是
value 是可以重复
,并且是可以修改的, 如果 一定要修改key 的值 , 就需要把 原先的key 删除 , 然后添加一个新的key
。 -
由于
key 是不可重复的
, 当我们返回 key 的集合 时, 就可以用set 来接收 ,因为set 的最大功能就是对元素 进行去重 , 达到每一个 关键码都是唯一性 的 -
由于 value 是可以重复的, 当我们返回
value的集合
时 , 不可以用 set 来接收, 一般用Collection 来接收 。 -
综上所得, 对于
set
的而言, 如果涉及到 单个元素的去重操作一般我们使用 set ,但如果有 两种关联属性 的话, 我们一般用
map
来 建立键值对进行操作 。
三. 哈希桶实现哈希表
哈希桶的本质就是当出现哈希冲突时, 利用 链表把新的关键码(key) 插入 到 原有的 key 的后面
, 把 一个大集合转化为一个小集合 来使用。
如果对这个概念还很模糊的小伙伴可以参考小编的上一篇文章哦 💕 💕 💕 💕
1. 哈希桶的代码展示
package hash;public class KVHashBucket <K,V>{// 存放链表数组Node<K,V>[]array;// 实际元素个数int useSize;private static final int DEFAULTSIZE=10;// 定义一个内部类为节点static class Node<K,V> {V val;Node<K,V> next;K key;public Node(K key,V val) {this.key=key;this.val = val;}}public KVHashBucket() {array= (Node<K, V>[]) new Node[DEFAULTSIZE];}/*** 插入方法* @param val 需要插入的数据*/void put(K key,V val) {// 判断是否满if (isFull()) {insertFunc2(key,val);return;}insertFunc1(key,val);}/*** 未满时普通插入* @param val 需要插入的数据*/private void insertFunc1(K key,V val) {int sz=array.length;int k=key.hashCode();int index= k % sz;// 如果出现 key 重复 就修改 val 值Node<K,V> cur=array[index];while (cur != null) {if (cur.key == key) {cur.val=val;return;}cur=cur.next;}Node<K,V> node=new Node<K,V>(key,val);node.next=array[index];array[index]=node;useSize++;}/*** 扩容方法* @param val 插入数据* 先扩容一个新数组* 然后把旧数组整合放入新数组* 最后旧数组成为新数组*/private void insertFunc2(K key ,V val) {Node<K,V> cur=null;Node<K,V>[] newArray= (Node<K, V>[]) new Node[2 * array.length];int newLength = 2 * array.length;for (int i = 0; i < array.length; i++) {cur=array[i];while (cur != null) {K m = cur.key;// 先让 node 记住 cur 的下个节点Node<K,V> node = cur.next;int k=m.hashCode();int index= k % newLength;cur.next = newArray[index];newArray[index] = cur;cur=node;}}Node<K,V> til=new Node<K,V>(key,val);int k=key.hashCode();int index=k%newLength;// 如果出现 key 重复 就修改 val 值Node<K,V> curN=array[index];while (curN != null) {if (curN.key == key) {curN.val=val;return;}curN=curN.next;}til.next=newArray[index];newArray[index]=til;useSize++;array=newArray;}/*** 查找该节点* @param key 需要查找的数据* @return 返回该数据的节点*/public V get(K key) {if (isEmpty()) return null;int k=key.hashCode();int index = k % array.length;Node<K,V> cur=array[index];while (cur != null) {if (cur.key.equals(key)) {return cur.val;}cur=cur.next;}return null;}/*** 查找是否该节点* @param key 需要查找的数据* @return 返回是否找到*/public boolean contains(K key) {if (isEmpty()) return false;int k=key.hashCode();int index=k % array.length;Node<K,V> cur=array[index];while (cur != null) {if (cur.val.equals(key)) {return true;}cur=cur.next;}return false;}public boolean remove(K key) {if (isEmpty()) return false;int k=key.hashCode();int index = k % array.length;Node<K,V> cur = array[index];Node<K,V> node = cur;if (cur.key==key) {array[index]=cur.next;return true;}while (cur != null) {if (cur.key.equals(key)) {node.next = cur.next;useSize--;return true;}node = cur;cur = cur.next;}return false;}/*** 判断是否满* @return 返回结果*/private boolean isFull() {return (useSize*1.0/array.length) >= 0.75;}/*** 判断是否为空* @return 返回结果*/private boolean isEmpty() {return useSize==0;}
}
public class TestKVHash {public static void main(String[] args) {KVHashBucket<String,String> kvhb=new KVHashBucket<>();kvhb.put("钟大帅哥","喜欢美女!");kvhb.put("郭大男神","喜欢拉比!");kvhb.put("刘大少爷","喜欢花钱!");}}
具体的实现 流程三部曲 :
-
首先创建一个
Node 类的节点
-
然后创建一个 Node类型的数组来存储
-
对利用 key 进行hashcode寻找到索引值, 根据索引操作链表 进行 增删查改 。
鱼式疯言
上述流程,小编只 强调三点
-
负载因子一旦达到
0.75
就相当于 满了, 就需要 扩容 -
扩容的过程中, 需要将 旧表转移到新表 中,不可以只是扩容而不转移, 否则数据会集中在原数组中, 而不会分散到新扩扩容的数组空间中。
-
如果添加时,遇到 相同的key 时, 只需要修改 原先 value 成 现有的 value , key 无须变动 即可。
其他小编认为问题不大,小伙伴们可以自己先 操练操练 哦, 如果有不清楚的地方, 欢迎评论区留言哦 。💖 💖 💖
总结
-
hashset
: 对于Hashset 来说主要是运用 Hashset 实现Set的相关的方法 来达到判断数据是否重复
和 进行去重操作的目的 。 -
HashMap : 对于
hashMap
实现 Map 接口, 达到对于键值对之间的关联
, 已经对于 键的唯一性 所包含 值的可重复性 查找操作。 -
哈希桶的实现: 利用数组和泛型来理解 哈希函数的底层实现 , 并注意实现过程中
负载因子的更新
以及背后的细节。
如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正
希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖
相关文章:

【数据结构】Java的HashMap 和 HashSet 大全笔记,写算法用到的时候翻一下,百度都省了!(实践篇)
本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…...

Docker 教程:如何查看容器的最后 300 行实时日志
Docker 教程:如何查看容器的最后 300 行实时日志 文章目录 Docker 教程:如何查看容器的最后 300 行实时日志Docker 日志简介查看容器日志的基本命令查看最后 300 行实时日志的具体命令参数解释 实际案例演示示例输出 常见问题解答如何退出实时日志的查看…...

Qwen2-VL论文阅读笔记
第1章介绍 论文亮点: 1、 the Naive Dynamic Resolution mechanism 2、Multimodal Rotary Position Embedding (M-RoPE) 2D Rotary Position Embedding 3、统一图片和视频的处理范式、增i强视觉感知能力 4、LVLMs的scaling laws:2B、8B、72B 5、 dynamic…...

APScheduler、Django实现定时任务,以及任务动态操作
环境:Windows 11、python 3.12.3、Django 4.2.11、 APScheduler 3.10.4 背景:工作需要使用且用法较为复杂,各种功能基本都使用了 事件:20240920 说明:记录,方便后期自己查找 1、搭建基础环境 文件结构图…...

SpringBoot开发——整合Apache POI轻松生成精美的Excel报表
文章目录 1、准备工作2、编写代码2.1 创建实体类2.2 创建Excel生成服务2.3 创建控制器 3、测试4、结论 在许多企业应用程序中,导出数据到Excel表格是一项常见的需求。Spring Boot提供了许多库来简化这个过程,其中包括Apache POI和Spring Boot的相关模块。…...

海信智能电视的使用心得
买了海信智能电视(型号:32E2F)有一段时间了,要使用这个智能电视还真能考验你的智商。海信电视有很多优点,它的屏幕比较靓丽,色彩好看,遥控器不用对着屏幕就能操作。但也有不少缺点。 1. 海信智能电视会强迫自动更新操作系统&…...

【YashanDB知识库】客户端字符集与数据库字符集兼容问题
本文转自YashanDB官网,具体内容请见https://www.yashandb.com/newsinfo/7352675.html?templateId1718516 问题现象 客户端yasql配置字符集为GBK,服务端yasdb配置字符集为UTF8,之后执行语句: 会发现: 期望是两个都…...

Session和Cookie是什么?有什么区别?分布式Session问题又是什么?
Session和Cookie是什么?有什么区别?分布式Session问题又是什么? Cookie:是服务器发送到浏览器并保存在本地的数据。在浏览器下一次向同一服务器再次发送请求时,将Cookie也发送给服务器,并以此来判定这个请…...

项目实战:Qt+OSG爆破动力学仿真三维引擎测试工具v1.1.0(加载.K模型,子弹轨迹模拟动画,支持windows、linux、国产麒麟系统)
若该文为原创文章,转载请注明出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/142454993 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、Op…...

CSS开发全攻略
目录 一、CSS基础入门(一)CSS概述1.CSS的定义与作用2.CSS的历史与发展3.CSS的核心概念(1)选择器(Selector)(2)声明(Declaration)(3)规…...

OpenCV运动分析和目标跟踪(3)计算图像序列的加权平均值函数accumulateWeighted()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 更新一个运行平均值。 该函数计算输入图像 src 和累积器 dst 的加权和,使得 dst 成为帧序列的运行平均值: dst ( x , y…...

vue3中echarts柱状图横轴文字太多放不下怎么解决
问题:在做数据展示的时候,使用的是echarts,遇到了个问题,就是数据过多,但是设置的x轴的文字名称又太长,往往左边第一个或右边最后一个的名称展示不全,只有半个。 从网上找到了几种办法ÿ…...

Web 开发安全与最佳实践:MVC、会话管理与常见攻击防御
1. 引言 随着 Web 应用的普及,安全问题变得尤为重要。从小型个人网站到复杂的企业级系统,安全漏洞可能导致数据泄露、服务中断甚至经济损失。因此,在 Web 开发中,采用良好的架构设计、会话管理和安全防护机制至关重要。本文将探讨…...

Segformer双显卡推理速度测试
1、4080单显卡和双显卡同步并行推理平均耗时分别为360ms、600ms;双显卡速度明显比单显卡的速度快 2、两个相机间隔500ms的并行推理耗时,单双显卡推理平均耗时为340ms 3、4080双显卡和4070双显卡同步并行推理平均耗时分别为360ms、380ms;4080比4070的速度快20ms...

使用在线电子模拟器 Wokwi 运行 ESP32 示例(Arduino IDE、ESP32C3)
文章目录 Wokwi安装客户端(Mac/Linux)创建 Token ESP32C3 示例demo.ino创建模拟器运行模拟器 Wokwi Wokwi 是一款在线电子模拟器。您可以使用它来模拟 Arduino、ESP32、STM32 以及许多其他流行的主板、部件和传感器。 Github: https://gith…...

vue3+element-plus icons图标选择组件封装
一、最终效果 二、参数配置 1、代码示例 <t-select-icon v-model"selectVlaue" />2、配置参数(Attributes)继承 el-input Attributes 参数说明类型默认值v-model绑定值string-prefixIcon输入框前缀iconstringSearchisShowSearch是否显…...

Spring validation校验框架
第1步:导入依赖 <!-- 校验框架--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> 第2步:为需要校验的参数&…...

UBUNTU20.04安装CH384串口卡驱动
继续上文:统信UOS安装CH384串口卡驱动-CSDN博客 统信UOS系统成功安装CH384串口驱动后,继续在ubuntu20.04下安装驱动,发现一直报错,原因是内核驱动不一致。 解决办法: 1. 下载最新的驱动。CH35XCH384驱动源文件资源-C…...

JWT(JSON Web Tokens) 详细介绍
文章目录 什么是JWT?JWT的组成部分JWT的使用场景优点缺点 Java中如何实现JWT编解码引入JJWT依赖编码JWT解码JWT使用示例 什么是JWT? JWT(JSON Web Tokens)是一种用于双方之间安全传输信息的简洁的、URL安全的令牌标准。JWT可以传递信息,这…...

数据结构练习题————(二叉树)——考前必备合集!
今天在牛客网和力扣上带来了数据结构中二叉树的进阶练习题 1.二叉搜索树与双向链表———二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com) 2.二叉树遍历————二叉树遍历_牛客题霸_牛客网 (nowcoder.com) 3.二叉树的层序遍历————102. 二叉树的层序遍历 - 力扣&am…...

一天认识一个硬件之鼠标
今天来给大家分享一下鼠标的相关内容,先来分享一下什么是鼠标: 鼠标是一种计算机输入设备,用于控制屏幕上的光标移动和进行各种操作。它最早由道格拉斯恩格尔巴特在1963年发明,并在1968年12月9日制成了世界上第一个鼠标。介绍完了…...

Django 请求配置
http请求配置 请求流程 urls.py配置 from first_app import viewsurlpatterns [path(admin/, admin.site.urls),path(test/,views.first_test), ] views.py配置 from django.shortcuts import render,HttpResponse# Create your views here. def first_test(request):prin…...

轮播图组件更加完善版
依然是基于微博语法开发,使用时请注意标签替换 优化了滑动的效果,默认的索引,滑动距离等, 使用方式和以前一样没变,主要修改了组件内部 <template><wbx-view class"" style"width: 100vw;heig…...

cpu路、核、线程
路:主板插口实际插入的 CPU 个数,也可以理解为主板上支持的CPU的数量。每个CPU插槽可以插入一个物理处理器芯片。例如,一台服务器可能有2路或4路插槽,这意味着它最多可以安装2个或4个物理处理器。 核:单块 CPU 上面能…...

鸿蒙开发(NEXT/API 12)【硬件(注册出行业务事件监听)】车载系统
注册出行业务事件监听,用于接收业务发送事件的通知。 接口说明 接口名描述[on] (type: ‘smartMobilityEvent’, smartMobilityTypes: SmartMobilityType[],callback: Callback): void应用注册出行业务事件监听。 开发步骤 导入Car Kit模块。 import { smartMobi…...

安卓中有main函数吗?
在标准的Android应用程序开发中,并不直接使用类似于传统Java或C程序中的main函数入口点。Android应用程序是基于组件的架构,它依赖于几个关键组件来执行不同的任务,这些组件包括Activity、Service、Broadcast Receiver和Content Provider。 …...

js-17-对数组、对象进行浅拷贝和深拷贝
目录 数组一、浅拷贝1. 展开运算符...2. Array.prototype.slice() 二、深拷贝1. JSON方法2. 递归函数 对象一、浅拷贝1. Object.assign()2. 展开运算符... 二、深拷贝1. JSON方法2. 递归函数 自己总结的一些方法,可能有不到位的地方,欢迎指出 数组 一、…...

Ubuntu24.04中安装Electron
1. 安装Nodejs 使用代理服务从github下载并执行Nodejs安装脚本(假设代理服务器为192.168.2.150:10792) curl -x 192.168.2.150:10792 -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash #注意,Nodejs官网的安装命令少了下面这一行: …...

CPU中也应用到了缓存:CPU3层高速缓存,以及它的缓存一致性问题、MESI协议和Java的一些应用
为什么需要cpu高速缓存? 缓存,一般是为了用来协调两端的数据传输效率差(也可以归纳为性能差),提升响应速度。那么CPU的高速缓存是用来协调什么之间的速度差呢? CPU在处理一条指令的时候,会读写…...

如何使用开发者工具捕获鼠标右键点击事件
要使用浏览器的开发者工具捕获鼠标右键点击事件,请按照以下步骤操作: 打开开发者工具 在大多数浏览器中,您可以按 F12 键或右键单击页面并选择"检查"或"检查元素"。 切换到 Console 标签页 在开发者工具中,找到并点击 “Console” 标签。 添加事件监听器…...