张小飞的Java之路——第三十二章——Set——HashSet
wptr33 2025-05-27 18:13 4 浏览
写在前面:
视频是什么东西,有看文档精彩吗?
视频是什么东西,有看文档速度快吗?
视频是什么东西,有看文档效率高吗?
1. 介绍
诸小亮:下面我们介绍——Set 接口
张小飞:Set 也是 Collection 接口下最常用的子接口之一吗?
诸小亮:是的,它有以下特点:
- 存储的元素是无序的(存储的顺序和取出的顺序不一致)
- 第一个存放‘a’,使用迭代器取时,第一个取出的不一定是 ‘a’
- 不能通过下标获取元素,只能使用迭代器或者增强 for 循环
- 不允许存储重复元素(自动去重)
张小飞:这,完全跟 List 不同
诸小亮:不错,它们是两种容器,Set 按照数据结构(存储数据的方式)划分,常用子类有2个:
- HashSet:底层是哈希表,专业名词:散列表
- TreeSet:底层是二叉树结构
2. HashSet
诸小亮:我们先学习——HashSet
2.1. 无序且自动去重
张小飞:您刚才说 Set 无序而且会自动去除重复元素,能不能演示一下?
诸小亮:当然可以了,看下面代码
public static void main(String[] args) throws Exception {
Set set = new HashSet();
set.add("c");
set.add("a");
set.add("b");
set.add("a");
System.out.println(set);
}
结果:
张小飞:原来如此,确实会自动去除重复
诸小亮:另外, 使用迭代器获取元素时
结果:
诸小亮:我们第一个添加的是 ‘c‘,但是使用迭代器第一个取出来的是 ‘a’
张小飞:嗯嗯,明白了,Set 可以使用增强 for 循环吗?
诸小亮:把那个 ‘吗’字去掉,当然可以用了,比如:
2.2. 哈希表
2.2.1. 介绍
张小飞:为什么 Set 是无序的,而且可以自动去重呢?
诸小亮:因为它的底层数据结构是哈希表
- 哈希表:又叫散列表,其实本质:可变数组+链表,不过使用数组方式有些特殊
张小飞:哦?我倒要瞧瞧是怎么特殊了
诸小亮:使用哈希表存储数据的过程是这样的
- 先根据哈希算法对 数据 计算出一个hash值,也叫:hashcode
- Object 类中就有 hashCode 方法
- 然后,使用 hashcode % 数组长度得到一个index,元素就存到对应的 index 位置
如下图,想把 abc 这个字符串存到哈希表中,其过程:
- 先计算 abc 的 hashcode
- String类中复写了Object的 hashCode 方法,专门计算字符串的 hash 值,之后Set底层还会对hash值进一步加工,此处不详细解释
- 然后计算index,假如 index = 3,那么 abc 就会放到对应位置:
张小飞:原来如此,不过,存一个数据而已为什么要搞的这么复杂呢?
诸小亮:真相只有一个——提高查询速度
张小飞:???很快嘛?
诸小亮:比 List 快,比如:判断集合中是否包含abc,用 List 你怎么做?
张小飞:这简单,搞个 for 循环然后调用 contains 方法就行了
诸小亮:如果使用 Set 直接计算 hashcode 就行了,比 一个个调用 contains 方法快多了
张小飞:明白了,计算出 hashcode,在计算 index,就能判断出对应的位置有没有元素了
诸小亮:正是这个道理
2.2.2. 哈希冲突
张小飞:那如果两个不一样的数据,计算出的 hashcode 相同呢?
诸小亮:这中情况称为——哈希冲突
- 哈希冲突:两个元素本身不同,但是通过哈希算法计算的两个 hashcode 相同
张小飞:这种情况应该怎么办呢?
诸小亮:这时,会以链表方式继续存储数据,比如:
- 假设 abc 和 cba 两个字符串的hashcode一样,会调用元素的 equals 方法,判断它们的内容是否一样,如果不一样以链表方式继续存储,如下图
这时,abc 就是链表的头节点
张小飞:明白了,相当于数组中的每个元素都是一个链表
诸小亮:完全正确,来,我们总结一下哈希表存储元素的过程:
- 先调用目标元素的 hashCode 方法,最终拿到 index,判断否已经存在
- 不存在:存储
- 存在,再调用equals方法,判断跟已有元素是否相同
- 相同:不存储
- 不相同:添加到列表的尾节点
2.2.3. 优劣和扩容
张小飞:我想,哈希表也是有一些缺点的吧
诸小亮:是的,它的优缺点如下:
- 优点:查询效率高
- 缺点:数组的某些位置可能不会存储数据,造成内存浪费
张小飞:不是数组中每个位置都有一个链表吗?
诸小亮:因为计算下标时,并不能保证数组中的每个位置都有一个链表,所以。。。。。
张小飞:既然这样,它什么时候扩容呢?
诸小亮:默认情况下,当存储的元素数量达到总容量的 75% 时,数组会自动扩容为原来的 2 倍
- 数组初始化长度是16,16 * 0.75 =12,当存储第13个元素时候,数组自动扩容为 32 长度
张小飞:明白了
2.3. 存储自定义对象
张小飞:Set 也可以存储自定义对象吧
诸小亮:当然可以啊
张小飞:那会自动去重吗?
诸小亮:必须的,你到底想说什么?
张小飞:那,为什么我下面的代码没有自动去重呢?
public static void main(String[] args) throws Exception {
//存储Hero对象,如果name一样,视为同一人
Set set = new HashSet();
set.add(new Hero("妲己"));
set.add(new Hero("西施"));
set.add(new Hero("嫦娥"));
set.add(new Hero("妲己"));
System.out.println(set);
}
结果:
诸小亮:原来你说的是这个问题啊,我问你,HashSet 存储数据的第一步是什么?
张小飞:计算目标元素的 HashCode 最终获取一个 index
诸小亮:你没有在 Hero 类中重写 hashCode 方法吧
张小飞:额。。。。,没有
诸小亮:这就是了,默认调用的是 Object 中的 hashCode 方法,从而导致 hashcode 不同
张小飞:那应该怎么办呢?
诸小亮:在 Hero 中复写 hashCode 方法,比如:
@Override
public int hashCode() {
return name.hashCode();//name的hashcode就是整个对象的hashcode
}
再次运行,结果:
张小飞:明白了
2.4. LinkedHashSet(了解)
诸小亮:HashSet 还有一个子类——LinkedHashSet,底层是:哈希表 + 双向链表
- 其他说法:数组 + 双重链表
张小飞:什么是双重链表?
诸小亮:看下图
诸小亮:哈希表 = 数组 + 链表,LinkedHashSet中又融入了 双向链表,所以叫:双重链表
张小飞:原来如此,不过为什么这么做呢?
诸小亮:这样可以保证数据是有序的,比如使用 HashSet 时:
结果,嫦娥在西施前面:
诸小亮:如果使用LinkedHashSet
结果:
张小飞:为什么它能保证元素插入时的顺序呢?
诸小亮:主要是因为双向链表的存在
- 双向链表中的每个节点都有 before 和 after 属性
- 在添加数据时,先求 hash 值,再计算 index,然后把数据放到双向链表中
- 遍历时,先拿到头结点,然后就可以有序的拿到所有元素
张小飞:原来如此,明白了
相关推荐
- 数据库基础:mysql主从集群搭建
-
文章首发于微信公众号:java架构师进阶之路前言:Mysql数据库没有增量备份的机制,当数据量太大的时候备份是一个很大的问题。还好mysql数据库提供了一种主从备份的机制,其实就是把主数据库的所有的...
- Mysql-cluster搭建
-
前期准备准备五台虚拟机:ip地址分别为:192.168.1.211管理节点192.168.1.64SQL节点192.168.1.65SQL节点192.168.1.70数据节点192.168.1...
- mysql 主从数据库搭建
-
一、创建目录在dev/htb下面创建文件夹master01htb]#mkdirmysql/master01-p2)进入master01...
- 从零搭建高可用的 MySQL 主从复制架构(基于 Linux 实战指南)
-
背景在生产环境中,单点MySQL数据库容易成为性能瓶颈或单点故障源。搭建MySQL主从复制架构,可以实现读写分离、高可用,提升系统的整体稳定性与扩展性。...
- 「MySQL 8」MySQL 5.7都即将停只维护了,是时候学习一波MySQL 8了
-
MySQL8新特性选择MySQL8的背景:MySQL5.6已经停止版本更新了,对于MySQL5.7版本,其将于2023年10月31日停止支持。后续官方将不再进行后续的代码维护。另外,...
- Mysql启动选项和配置文件
-
Mysql启动选项和配置文件Mysql启动方式下面的启动命令都需要依赖在Linux环境下配置的Mysql环境变量...
- centos安装mysql操作手册
-
1.下载Mysql首先去Mysql官网下载安装包,网址https://dev.mysql.com/downloads/mysql/推荐大家下载Linux通用版本的,便于管理安装位置,也方便一台服务器...
- MySQL安装
-
MySQL的安装过程因操作系统的不同而有所差异。以下是在几种常见操作系统上安装MySQL的基本步骤:Windows下载MySQL:访问MySQL官方网站下载页面:MySQLDownloads...
- MySQL数据库安装教程
-
前言今天就带各位小伙伴学习数据库技术。数据库技术是Java开发中必不可少的一部分知识内容。也是非常重要的技术。...
- MySQL学到什么程度?才有可以在简历上写精通
-
前言如今互联网行业用的最多就是MySQL,然而对于高级Web面试者,尤其对于寻找30k下工作的求职者,很多MySQL相关知识点基本都会涉及,如果面试中,你的相关知识答的模糊和不切要点,基...
- 一起免费考 MySQL OCP 认证啦
-
前言:在1995年,首个MySQL版本发布,为庆祝MySQL诞辰30周年,OracleUniversity在限定期间内推出了多个MySQL的免费培训课程与认证,其中也包括My...
- 教程2 | 制作用户管理系统
-
一、项目简介用户管理系统是一个基于C/S模式的小型管理系统,使用了GUI技术来实现管理系统的页面效果,该管理系统可以对用户的信息,比如姓名、年龄、密码和地址等进行增删改查操作。用户管理系统通过JDBC...
- 红帽Linux中安装mysql8详细步骤
-
注意:我写的解压路径和截图路径不一致,仅供参考先前往官网下载mysql8下载地址:https://dev.mysql.com/downloads/选择指定版本和系统下载命令...
- MySQL主从配置
-
主从原理MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主从后,在A上写数据,另外一台B也会跟着写数据,两者数据实时同步的。...
- mysql的主从搭建以及实现主从切换方法
-
主从搭建的方法:a.准备两台服务器...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git 执行pull错误如何撤销 git pull fail
-
面试官:git pull是哪两个指令的组合?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)