【JAVA基础】集合类之Hash的原理及应用

近期几期内容都是围绕该体系进行知识讲解,以便于同学们学习Java集合篇知识能够系统化而不零散。
在这里插入图片描述

本文将介绍HashSet的基本概念,功能特点,使用方法,以及优缺点分析和应用场景案例。

一、概念

HashSet是 Java 集合框架中的一个重要成员,它实现了Set接口。Set接口的主要特点是不允许包含重复元素,而HashSet以哈希表的方式来存储元素,这使得它在存储和检索元素时具有高效的性能。

与LinkedHashSet 的区别:

  • HashSet 底层是由HashMap实现的,通过对象的hashCode方法与equals方法来保证插入元素的唯一性,无序(存储顺序和取出顺序不一致),。
  • LinkedHashSet 底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序。(存储和取出是一致)

二、存储方式

哈希函数

(1)HashSet使用哈希函数来计算元素的哈希值。哈希函数是一种将任意长度的数据映射为固定长度哈希值的函数。对于要存储的每个元素,HashSet会调用其哈希函数来获取一个哈希值。
(2)例如,对于整数类型,哈希函数可能会简单地对整数进行一些计算来得到哈希值;对于对象类型,默认会使用对象的hashCode()方法来计算哈希值。

哈希表结构

(1)HashSet内部使用哈希表来存储元素。哈希表是一个数组,每个数组元素称为一个 “桶”(bucket)。
(2)当一个元素被添加到HashSet时,首先通过哈希函数计算出该元素的哈希值,然后根据哈希值确定它应该存储在哪个桶中。
(3)如果多个元素的哈希值相同,它们会被存储在同一个桶中。在这种情况下,HashSet会通过比较元素的equals()方法来确保集合中不包含重复元素。只有当两个元素的哈希值相同且equals()方法返回true时,才被认为是重复元素。

三、源码分析

构造方法

HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的新 set。
HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。 放

实现原理

(1)往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。见下面2种情况:
情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。

HashSet 中 hashCode () 与 equals () 方法的调用时机

添加元素时

  • 哈希值计算与哈希冲突判断
    当向HashSet中添加一个元素时,首先会调用该元素的hashCode()方法来获取其哈希值。这个哈希值用于确定元素在HashSet内部哈希表中的存储位置(桶的位置)。
    例如,有一个自定义类Person的实例要添加到HashSet中:
public class Person {int id;String name;public Person(int id, String name) {super();this.id = id;this.name = name;}@Override public String toString() {return "{ 编号:" + this.id + " 姓名:" + this.name + "}";}@Override public int hashCode() {System.out.println("=======hashCode方法被调用了=====");return this.id;}@Override public boolean equals(Object obj) {System.out.println("======equals方法被调用了======");Person p = (Person) obj;return this.id == p.id;}
}

原始的判断是否能添加的逻辑:
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。即,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

当执行 set.add(new Person(110, “Alice”));时,会先调用Person类的hashCode()方法计算哈希值
在上面HashCode是自己重写过的,将ID直接当做Hash值;
同时重写了equals方法,因为add的时候 先调用HashCode方法判断Hash值是否一致,如果一致,则通过判断equals的结果是否为true,如果为true则代表元素重复,拒绝添加!
重写的equals方法:如果ID相同则元素相同

如果不同元素计算出的哈希值相同(哈希冲突),此时HashSet会进一步调用这些元素的equals()方法来判断它们是否真正相等。只有当两个元素的哈希值相同且equals()方法返回true时,才认为这两个元素是重复的,不会将新元素添加到HashSet中。
假设在HashSet中已经存在一个Person对象p1( id= 25,name = “Alice”),现在要添加另一个Person对象p2【new Person(220, “GOD”)】 p3对象【new Person(330, “LiMing1”)】、p4对象【new Person(110, “LiMing2”)】;

验证:


class Demo2 {public static void main(String[] args) {HashSet set = new HashSet();boolean alice = set.add(new Person(110, "Alice"));boolean god = set.add(new Person(220, "GOD"));boolean liMing1 = set.add(new Person(330, "LiMing1"));boolean liMing2 = set.add(new Person(110, "LiMing2"));//在现实生活中只要编号一致就为同一个人.System.out.println("添加成功吗?" +liMing1);System.out.println("添加成功吗?" +liMing2 );System.out.println("集合的元素:" + set);}
}

可见结果如下:
解释:
因为add了四次,所以有调用四次hashCode方法,来计算HashCode,在我们的代码里面重写了HashCode方法,id当做了HashCode的值,如果HashCode的值一致,则调用equals方法,判断id是否一致,如果一致则判断元素重复,添加失败。因为有两个110的id,所以第一个添加成功,第二个添加失败
在这里插入图片描述
如果id改完不一致的,则添加成功。即使name一致(因为重写的equals是通过判断ID是否一致来判断元素重复与否的。)
在这里插入图片描述

四、使用场景

去重

当需要去除一个集合中的重复元素时,HashSet是一个很好的选择。例如,在处理一组用户输入的数据时,如果不希望有重复的数据,可以将数据存储在HashSet中。


class Demo2 {public static void main(String[] args) {System.out.println("=======================================");
//      LinkedHashSet去重  去重后保持原有顺序(重复数据只保留一条)String[] arr = new String[] {"i", "think", "i", "am", "the", "best"};Collection<String> noDups = new LinkedHashSet<String>(Arrays.asList(arr));System.out.println("(LinkedHashSet) distinct words:    " + noDups);System.out.println("=======================================");
//       去重后顺序打乱(重复数据只保留一条)String[] arr2 = new String[] {"i", "think", "i", "am", "the", "best"};Collection<String> noDups2 = new HashSet<String>(Arrays.asList(arr2));System.out.println("(HashSet) distinct words:    " + noDups2);System.out.println("=======================================");
//      去重后顺序打乱(重复数据只保留一条)String[] arr3 = new String[] {"i", "think", "i", "am", "the", "best"};Set<String> s = new HashSet<String>();for (String a : arr3){if (!s.add(a)){System.out.println("Duplicate detected: " + a);}}System.out.println(s.size() + " not distinct words: " + s);
//        去重后顺序打乱(相同的数据一条都不保留,取唯一) ,能把重复的元素剔除出去;同时把哪些元素重复过滤出来System.out.println("=======================================");String[] arr4 = new String[] {"i", "think", "i", "am", "the", "best"};Set<String> uniques = new HashSet<String>();Set<String> dups = new HashSet<String>();for (String a : arr4){{if (!uniques.add(a))dups.add(a);}}uniques.removeAll(dups);System.out.println("Unique words:    " + uniques);System.out.println("Duplicate words: " + dups);}
}

结果:
在这里插入图片描述

快速查找

由于哈希表的特性,HashSet在查找元素时具有非常高的效率。平均时间复杂度接近常数时间。
比如在一个大型数据集中快速判断某个元素是否存在

集合运算

在进行一些集合相关的运算时,HashSet也很有用。例如,可以使用HashSet来计算两个集合的交集、并集和差集等。

假设有两个集合set1和set2,计算它们的交集:

Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));Set<Integer> intersectionSet = new HashSet<>(set1);
intersectionSet.retainAll(set2);
System.out.println("Intersection set: " + intersectionSet); // 输出 [4, 5]

在多线程环境下的使用示例(注意需要适当的同步措施)

import java.util.HashSet;
import java.util.Set;public class HashSetInMultiThreadExample {public static void main(String[] args) {Set<Integer> sharedSet = new HashSet<>();// 创建多个线程并向集合中添加元素Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {sharedSet.add(i);}});Thread thread2 = new Thread(() -> {for (int i = 1000; i < 2000; i++) {sharedSet.add(i);}});// 启动线程thread1.start();thread2.start();// 等待线程完成try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出集合中的元素数量System.out.println("Size of the set after multi-threaded operations: " + sharedSet.size());}
}

需要注意的是:HashSet是线程不安全的,如果涉及到多线程需要使用ConcurrentHashSet;
在这个多线程示例中,如果不进行适当的同步,可能会导致数据不一致等问题。在实际应用中,可以考虑使用ConcurrentHashSet等线程安全的集合类或者通过其他同步机制来确保线程安全。

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

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

相关文章

思科防火墙:ASA中Object-group在ACL中的应用

一、实验拓扑&#xff1a; 二、实验要求&#xff1a; 先定义几个小的&#xff0c;然后用大的包在一起&#xff1b;打包在一起&#xff0c;这就是所谓的嵌套&#xff0c;嵌套在编程里是很长用的东西&#xff0c;叫做Object-group&#xff1b; Object-group比较强大&#xff0c;可…

【JAVA开源】基于Vue和SpringBoot的师生共评作业管理系统

本文项目编号 T 071 &#xff0c;文末自助获取源码 \color{red}{T071&#xff0c;文末自助获取源码} T071&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

学习threejs,模拟窗户光源

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言二、&#x1f340;绘制任意字体模型…

性能测试度量指标的多种收集环境

目录 一、技术环境 二、业务环境 三、操作环境 在用卷尺测量某一物体的长度时&#xff0c;长度就是该场景下的度量指标&#xff0c;我们可以用分米、米或者更精确的厘米甚至毫米来描述这个长度&#xff0c;具体取决于使用场景。 与其他形式的测量一样&#xff0c;对性能进行…

双十一购物狂欢节开始,盘点有哪些值得购买的母婴好物

随着双十一全球购物狂欢节的脚步日益临近&#xff0c;各大电商平台正紧锣密鼓地筹备一系列引人瞩目的促销活动。这一时刻不仅是全民欢腾的消费庆典&#xff0c;更是年轻父母为家庭添置高品质母婴用品的理想契机。对于追求生活品质的家庭而言&#xff0c;挑选既安全又具成本效益…

01移动零

题目链接 代码&#xff1a; class Solution {public void moveZeroes(int[] nums) {for(int cur0,dest-1;cur<nums.length;cur) {//判断nums(cur)是否为0if(nums[cur]!0) {dest;swap(cur,dest,nums);//进行交换}}}public void swap(int cur,int dest,int []array) {int te…

【2024最新】基于springboot+vue的交流互动系统lw+ppt

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

计算机网络-------重传、TCP流量控制、拥塞控制

重传、滑动窗口、流量控制、拥塞避免 重传机制 超时重传 发送方在发送数据时会启动一个定时器&#xff0c;当超过指定的时间之后&#xff0c;还没接收到接收方的ACK确认应答报文&#xff0c;就会重传该数据 快重传 当发送方收到接收方三个连续的ack之后说明发送方发送的报…

从零开始构建:Python自定义脚本自动化你的日常任务

从零开始构建&#xff1a;Python自定义脚本自动化你的日常任务 Python 作为一种简洁且功能强大的编程语言&#xff0c;被广泛应用于各种自动化任务中。通过编写 Python 脚本&#xff0c;你可以轻松地将日常重复性工作自动化&#xff0c;例如文件操作、数据处理、网络爬虫、系统…

第五届智能设计国际会议(ICID 2024)

文章目录 一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题六、咨询 一、会议详情 二、重要信息 大会官网&#xff1a;https://ais.cn/u/vEbMBz提交检索&#xff1a;EI Compendex、IEEE Xplore、Scopus大会时间&#xff1a;2024年10月25-27日大会地点&#xff1…

【高效转换神器】MaxToCAD插件:一键将3dMax三维模型秒变Autocad二维平面图

3dMax转CAD平面图插件MaxToCAD是一款功能强大的工具&#xff0c;它能够将3dMax中的三维模型快速转换为Autocad可识别的二维平面图。以下是对该插件的详细介绍&#xff1a; 一、功能概述 MaxToCAD插件允许用户轻松地将3dMax中的三维对象转换为CAD软件中的二维图形。这对于需要…

有限差分法 - 拉普拉斯算子 (Part 1)

Finite difference method - Laplacian part 1 — ROCm Blogs (amd.com) 2022年11月14日, Justin Chang, Rajat Arora, Thomas Gibson, Sean Miller, Ossian O’Reilly撰写。 有限差分法是一种在计算物理中常用的网格离散化方法&#xff0c;广泛应用于从地球物理&#xff08;天…

比亚迪「召回」热销车!谁担责

作为整车关键的安全件&#xff0c;底盘系统是支持行车安全与舒适的基石。相比于主、被动安全系统&#xff0c;底盘系统的故障&#xff0c;更容易直接导致事故风险的急剧上升。 9月29日&#xff0c;比亚迪发布召回公告&#xff0c;召回2023年2月4日至2023年12月26日期间生产的部…

遗传算法与深度学习实战(16)——神经网络超参数优化

遗传算法与深度学习实战&#xff08;16&#xff09;——神经网络超参数优化 0. 前言1. 深度学习基础1.1 传统机器学习1.2 深度学习 2. 神经网络超参数调整2.1 超参数调整策略2.2 超参数调整对神经网络影响 3. 超参数调整规则小结系列链接 0. 前言 我们已经学习了多种形式的进化…

添加菜品到购物车

分析 数据库设计 代码开发 三个步骤:判断当前商品是否已经在购物车中如果在购物车中,更新购物车中商品数量如果不在购物车中,添加到购物车controller层 /*** 添加购物车** @return*/@PostMapping("/add")@ApiOperation("添加购物车")public Result add(…

碰撞检测 | 图解视线生成Bresenham算法(附ROS C++/Python/Matlab实现)

目录 0 专栏介绍1 Bresenham算法介绍2 图解Bresenham算法3 算法流程4 仿真实现4.1 ROS C实现4.2 Python实现4.3 Matlab实现 0 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff…

C语言 | 第十一章 | static 日期函数 数学函数

P 100 变量作用域基本规则 2023/1/9 一、基本介绍 概念&#xff1a;所谓变量作用域&#xff08;Scope&#xff09;&#xff0c;就是指变量的有效范围。 函数内部声明/定义的局部变量&#xff0c;作用域仅限于函数内部。 #include<stdio.h> void sayHello() {char nam…

手机怎样改网络ip地址?内容详尽实用

随着网络技术的发展&#xff0c;更改手机IP地址已成为一种常见需求。本文将详细介绍如何在不同网络环境下更改手机IP地址&#xff0c;包括移动网络和WiFi网络&#xff0c;以及同时适用于两种网络的方法&#xff0c;内容详尽实用&#xff0c;干货满满。 一、适用于移动网络&…

为什么目录站这么多导出链接,却不影响排名?

导出链接就是网站或者页面中有指向别的网站的单向链接&#xff0c;导出链接过多会导致网站的权重流向对方的网站&#xff0c;所以除非您网站内容有极大的参考价值&#xff0c;并且专业性很强&#xff0c;在业界有口皆碑&#xff0c;否则很难让别的站长主动单向链接到您的网站。…

ChatGPT助力文献综述写作:提升效率与写作技巧!

文献综述在论文写作中占有举足轻重的地位。它不仅帮助我们梳理已有的研究成果&#xff0c;还能为自己的研究奠定基础。许多同学在撰写文献综述时常常感到头疼&#xff1a;如何处理海量的信息&#xff1f;如何将不同的观点有条理地整合起来&#xff1f;再加上学术语言的高要求&a…