JAVA小知识23:set与HashSet

一、Set

1.1、Set的基本知识

set也是单列集合的一种,用于存储一组不重复的元素。它是一种集合数据类型,常用于需要确保元素唯一性和快速查找的场景。他有如下特点:

  • 无序性:Set 中的元素是无序的,没有特定的顺序。
  • 唯一性:Set 中不允许有重复的元素。
  • 高效性:典型的 Set 实现(如哈希集合)可以在平均情况下提供 O(1) 时间复杂度的添加、删除和查找操作。
  • 无索引:Set当中没有索引,无法通过索引来操作数据。

1.2、Set集合的实现类

  • Hashset:无序、不重复、无索引
  • LinkedHashset:有序、不重复、无索引
  • Treeset:可排序、不重复、无索引

set继承自collection,他的api与collection相似,来回忆一下collection都有什么成员方法:

public boolean add(E e)          把给定的对象添加到当前集合中。
public void clear()              清空集合中所有的元素。
public boolean remove(E e)       把给定的对象在当前集合中册除。
public boolean contains(E e)     判断当前集合中是否包合给定的对象。
public boolean isEmpty()         判断当前集合是否为空。
public int size()                返回集合中元素的个数。
public Object[] toArray()        把集合中的元素,存储到数组中。

二、HashSet

2.1、哈希表与哈希值

HashSet是Set实现类的一种,其底层是基于哈希表存储数据。

哈希表是一种对增删改查性能都良好的结构。
哈希表的组成:

  • jdk8以前:数组+链表
  • jdk8以后:数组+链表+红黑树

哈希表的灵魂是哈希值(可以理解为对象的整数表现形式),其特点如下:

  • 根据hashcode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashcode方法,利用对象内部的属性值计算哈希值

对象哈希值的特点:

  • 如果没有重写hashcode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希冲突)

没有重写:

class Person {String name;Person(String name) {this.name = name;}
}
public class Main {public static void main(String[] args) {Person person1 = new Person("Alice");Person person2 = new Person("Alice");// 没有重写 hashCode 方法,不同对象计算出的哈希值是不同的System.out.println(person1.hashCode()); // 366712642System.out.println(person2.hashCode()); // 1829164700}
}

重写hashcode方法(自己编的hash算法,正常开发IDEA有选项可以自动重写hashcode与equals):

class PersonWithHashCode {String name;PersonWithHashCode(String name) {this.name = name;}@Overridepublic int hashCode() {return name != null ? name.hashCode() : 0;}
}
public class Main {public static void main(String[] args) {PersonWithHashCode person1 = new PersonWithHashCode("Alice");PersonWithHashCode person2 = new PersonWithHashCode("Alice");// 重写 hashCode 方法,不同的对象只要属性值相同,计算出的哈希值就是一样的System.out.println(person1.hashCode()); // 63281965System.out.println(person2.hashCode()); // 63281965}
}

2.2、HashSet 的特性

如果集合中存储的是自定义对象,必须要重写hashcode和equals方法

  • 唯一性:HashSet 中的每个元素都是唯一的,不允许重复元素。
  • 无序性:HashSet 不保证元素的顺序,即元素的存储顺序和插入顺序可能不同。
  • 高效性:HashSet 的查找、插入和删除操作平均时间复杂度为 O(1)。

2.3、HashSet的底层

当我们敲出这行代码的时候:

HashSet<String> hashSet = new HashSet<>();

第一步:他的底层会创建一个默认长度16,默认加载因子为0.75的数组,数组名table;
第二步:根据元素的哈希值跟数组的长度计算出应存入的位置;
第三步:判断存入的位置是否为null,如果为null直接存入;
第四步:如果位置不为null,表示有元素,则调用equals方法比较属性值;
第五步:如果一样则不存入,如果不一样则形成链表;

  • JDK8以前:新元素存入数组,老元素挂在新元素下面
  • JDK8以后:新元素直接挂在老元素下面

第六步:当数组长度超过 默认长度 * 加载因子(16 * 0.75=12)的时候,数组就会扩容到之前的两倍变成新的默认长度(32);
第七步:在jdk8以后,如果链表长度超过8而且数组长度超过64,则链表会转化为红黑树;

索引4下方为链表:
在这里插入图片描述
长度超过了8,转化为红黑树:
在这里插入图片描述

2.3、三个小问题:

2.3.1、HashSet为什么是无序的?

因为本身添加就是根据哈希值计算出来的地址,所以添加的时候顺序就是不固定的,但是取值的时候顺序是固定的,从数组索引0开始查找取值。

2.3.2、HashSet为什么没有索引?

因为HashSet不够纯粹,数组虽然有索引,但是数组元素下面任然挂着数据,无法确定索引。

2.3.3、HashSet利用什么来保证数据的去重的?

利用HashCode方法以及equals,通过HashCode算出哈希值,然后通过equals判断是否有相同的元素。

2.4、成员方法

方法说明
add(E e)将指定的元素添加到此集合中(如果尚未存在)。
clear()移除此集合中的所有元素。
contains(Object o)如果此集合包含指定的元素,则返回 true
isEmpty()如果此集合不包含元素,则返回 true
iterator()返回在此集合的元素上进行迭代的迭代器。
remove(Object o)如果指定的元素存在于此集合中,则将其移除。
size()返回此集合中的元素数量(集合的容量)。
clone()返回此 HashSet 实例的浅表副本:元素本身不被复制。
spliterator()创建一个 Spliterator 以按适当顺序遍历此集合中的元素。
toArray()返回包含此集合中所有元素的数组。
toArray(T[] a)返回包含此集合中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
removeIf(Predicate<? super E> filter)移除此集合中满足给定谓词的所有元素。
stream()返回包含此集合中所有元素的顺序流。
parallelStream()返回可能并行的包含此集合中所有元素的流。
// 创建一个新的HashSet
HashSet<String> set = new HashSet<>();// add(E e) 方法示例
set.add("Apple");
set.add("Banana");
set.add("Cherry");// contains(Object o) 方法示例
System.out.println("Contains 'Apple': " + set.contains("Apple")); // true
System.out.println("Contains 'Mango': " + set.contains("Mango")); // false// isEmpty() 方法示例
System.out.println("Is empty: " + set.isEmpty()); // false// iterator() 方法示例
Iterator<String> iterator = set.iterator();
System.out.print("Iterator values: ");
while (iterator.hasNext()) {System.out.print(iterator.next() + " ");
}
System.out.println();// remove(Object o) 方法示例
set.remove("Banana");
System.out.println("After removal of 'Banana': " + set); // [Apple, Cherry]// size() 方法示例
System.out.println("Size: " + set.size()); // 2// clone() 方法示例
HashSet<String> clonedSet = (HashSet<String>) set.clone();
System.out.println("Cloned set: " + clonedSet); // [Apple, Cherry]// spliterator() 方法示例
System.out.println("Spliterator estimate size: " + set.spliterator().estimateSize()); // 2// toArray() 方法示例
Object[] array = set.toArray();
System.out.println("Array: " + java.util.Arrays.toString(array)); // [Apple, Cherry]// toArray(T[] a) 方法示例
String[] stringArray = new String[set.size()];
set.toArray(stringArray);
System.out.println("String array: " + java.util.Arrays.toString(stringArray)); // [Apple, Cherry]// removeIf(Predicate<? super E> filter) 方法示例
Predicate<String> filter = str -> str.startsWith("A");
set.removeIf(filter);
System.out.println("After removeIf (str -> str.startsWith(\"A\")): " + set); // [Cherry]// clear() 方法示例
set.clear();
System.out.println("Is empty after clear: " + set.isEmpty()); // true

三、LinkedHashSet

3.1、LinkedHashSet的基本知识

LinkedHashSet继承自HashSet,上面我们说到LinkedHashSet是有序,不可重复的,其实这里的有序指的是存和取的顺序是有序的。
在这里插入图片描述
LinkedHashSet的底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。

索引8为第一个添加,索引3为第二个添加,当第二个添加完成后,索引8的值会记录索引3的地址值,索引3也会记录索引8的地址值。每一次添加,最后一个与倒数第二个都会相互记录地址值。
在这里插入图片描述

当LinkedHashSet遍历的时候,就会遍历双向链表,所以存和取的顺序就相同了。

3.2、成员方法

与HashSet差不多

方法说明
add(E e)将指定的元素添加到此集合中(如果尚未存在)。
clear()移除此集合中的所有元素。
contains(Object o)如果此集合包含指定的元素,则返回 true
isEmpty()如果此集合不包含元素,则返回 true
iterator()返回在此集合的元素上进行迭代的迭代器,按插入顺序迭代。
remove(Object o)如果指定的元素存在于此集合中,则将其移除。
size()返回此集合中的元素数量(集合的容量)。
clone()返回此 LinkedHashSet 实例的浅表副本:元素本身不被复制。
spliterator()创建一个 Spliterator 以按插入顺序遍历此集合中的元素。
toArray()返回包含此集合中所有元素的数组。
toArray(T[] a)返回包含此集合中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
removeIf(Predicate<? super E> filter)移除此集合中满足给定谓词的所有元素。
stream()返回包含此集合中所有元素的顺序流。
parallelStream()返回可能并行的包含此集合中所有元素的流。

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

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

相关文章

代码签名证书申请指南

申请代码签名证书的具体流程可以归纳为以下几个步骤&#xff1a; 1、确定证书类型&#xff1a; 根据您的需求选择合适的代码签名证书类型。常见的有OV&#xff08;Organization Validation&#xff0c;组织验证&#xff09;代码签名证书和EV&#xff08;Extended Validation&am…

【Java】已解决java.lang.IllegalAccessException异常

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.lang.IllegalAccessException异常 一、问题分析背景 在Java开发中&#xff0c;java.lang.IllegalAccessException是一个常见的运行时异常&#xff0c;它通常发生在尝试…

ARM32开发--FreeRTOS-事件组

系列文章目录 知不足而奋进 望远山而前行 目录 系列文章目录 文章目录 前言 目标 内容 概念 事件标志位 开发流程 功能介绍 创建事件组 触发事件 等待事件触发 同步 清理事件 案例 总结 前言 在嵌入式系统开发中&#xff0c;任务之间的同步和通信是至关重要的…

智慧矿山项目建设整体解决方案(938页 )

智慧矿山&#xff0c;究竟是什么&#xff1f; 在深入探讨之前&#xff0c;让我们先来提出一个深刻的问题&#xff1a;我们能否借助科技的力量&#xff0c;让矿山作业不仅安全、高效&#xff0c;还能做到环保可持续&#xff1f;答案是肯定的。智慧矿山&#xff0c;正是这一理念…

支撑每秒 600 万订单无压力,SpringBoot + Disruptor 太猛了!

一、背景 工作中遇到项目使用Disruptor做消息队列,对你没看错,不是Kafka,也不是rabbitmq;Disruptor有个最大的优点就是快,还有一点它是开源的哦,下面做个简单的记录. 二、Disruptor介绍 Disruptor 是英国外汇交易公司LMAX开发的一个高性能队列&#xff0c;研发的初衷是解决内存…

【docker入门】

在软件开发过程中&#xff0c;环境配置是一个至关重要的步骤&#xff0c;它不仅影响开发效率&#xff0c;也直接关联到软件的最终质量。正确的环境配置可以极大地减少开发中的潜在问题&#xff0c;提升软件发布的流畅度和稳定性。以下是几个关键方面&#xff0c;以及如何优化环…

【机器学习】第6章 支持向量机(SVM)

一、概念 1.支持向量机&#xff08;support vector machine&#xff0c;SVM&#xff09;&#xff1a; &#xff08;1&#xff09;基于统计学理论的监督学习方法&#xff0c;但不属于生成式模型&#xff0c;而是判别式模型。 &#xff08;2&#xff09;支持向量机在各个领域内的…

如何在不丢失数据的情况下解锁安卓手机密码

手机是我们生活中必不可少的工具&#xff0c;可以帮助我们与朋友和家人保持联系&#xff0c;了解最新消息&#xff0c;甚至经营我们的业务。然而&#xff0c;当我们在 Android 手机或 iPhone 上设置密码时&#xff0c;我们经常会忘记密码&#xff0c;或者根本没有设置密码。当这…

IntelliJ IDEA 使用 Maven 时不加载本地私服的最新版本快照(snapshot)JAR 包

IntelliJ IDEA 使用 Maven 时不加载本地私服的最新版本快照&#xff08;snapshot&#xff09;JAR 包 目录 IntelliJ IDEA 使用 Maven 时不加载本地私服的最新版本快照&#xff08;snapshot&#xff09;JAR 包1. 检查 settings.xml2. IDEA Maven 配置3. 强制更新 Snapshot4. 使用…

学习笔记——路由网络基础——路由度量值

3、路由度量值 (1)基本概念 路由度量值表示到达这条路由所指目的地址的代价。度量值数值越小越优先&#xff0c;度量值最小路由将会被添加到路由表中。度量值很多时候被称为开销(Cost)。 路由度量(路由开销 cost)对于同一个路由协议&#xff0c;当到达某目标网段有多条路由供…

SQL Server入门-安装和测试(2008R2版)

环境&#xff1a;win10&#xff0c;SQL Server 2008 R2 因为工作需要用到SQL Server&#xff08;而且要用2008R2版&#xff09;&#xff0c;完全不熟&#xff0c;所以来学习学习。 SQL Server是微软开发的关系型数据库&#xff0c;支持SQL。同时还有微软还开发了自己的T-SQL&am…

小鹏汽车2025冲刺类L4智驾,挑战与机遇并存

随着科技的飞速发展&#xff0c;智能驾驶已成为汽车行业的前沿领域。近日&#xff0c;小鹏汽车在AI DAY上宣布国内首个量产上车的端到端大模型&#xff0c;这一创新举措无疑为智能驾驶的发展注入了新的活力。然而&#xff0c;在迈向2025年实现类L4级智能驾驶的道路上&#xff0…

大前端 业务架构 插件库 设计模式 属性 线程

大前端 业务架构 插件库 适配模式之(多态)协议1对多 抽象工厂模式 观察者模式 外观模式 装饰模式之参考catagory 策略模式 属性

服务器新硬盘分区、格式化和挂载

文章目录 参考文献查看了一下起点现状分区(base) ~ sudo parted /dev/sdcmklabel gpt&#xff08;设置分区类型&#xff09;增加分区 格式化需要先退出quit&#xff08;可以&#xff09;(base) / sudo mkfs.xfs /dev/sdc/sdc1&#xff08;失败&#xff09;sudo mkfs.xfs /dev/s…

通过nginx转发后应用偶发502bad gateway

序言 学习了一些东西&#xff0c;如何才是真正自己能用的呢&#xff1f;好像就是看自己的潜意识的反应&#xff0c;例如解决了一个问题&#xff0c;那么下次再碰到类似的问题&#xff0c;能直接下意识的去找到对应的信息&#xff0c;从而解决&#xff0c;而不是和第一次碰到一样…

CRC循环冗余校验

CRC循环冗余校验 循环冗余校验码是一种用在数字网络和存储设备上的差错校验码&#xff0c;可以校验原始数据的偶然差 错。 CRC 计算单元使用固定多项式计算 32 位 CRC 校验码。 1. 硬件CRC 在单片机中&#xff0c;芯片具有专用的CRC计算单元&#xff0c;它是按照32位数据长…

LeetCode 48.旋转图像

1.做题要求: 2.从此题我们可以看出规律为第几行要变为倒数第几列&#xff0c;所以我们最好先把二维数组存入一维数组中&#xff0c;然后先从最后一列遍历&#xff0c;把一维数组里的元素&#xff0c;依次等于遍历的元素即可: void rotate(int** matrix, int matrixSize, int*…

Scala函数

文章目录 一、第1关&#xff1a;方法S 三角形 ​实验代码&#xff1a; 二、第2关&#xff1a;Scala函数以及函数调用实验代码&#xff1a; 一、第1关&#xff1a;方法 任务描述 本关任务&#xff1a;根据三角形的三边长 a、b、c&#xff0c;返回三角形的面积。 任意三角形面积…

外网怎么访问内网?

当我们需要在外网环境下访问内网资源时&#xff0c;常常会面临一些困扰。通过使用一些相关的技术与工具&#xff0c;我们可以轻松地实现这一目标。本文将介绍如何通过【天联】组网产品&#xff0c;解决外网访问内网的问题。 【天联】组网是一款由北京金万维科技有限公司自主研…

JAVAFX打包部署真正能用的办法(jdk21,javafx23)IntelliJ IDEA

我之前创建了javafx项目&#xff0c;想打包试试。一试&#xff0c;全是坑&#xff0c;所以记录下来&#xff0c;为有缘人节约时间。直接构建工件是错误的&#xff0c;别尝试了&#xff0c;找不在JDK的。我也花了一天多的时间尝试了网上各种大神的办法&#xff0c;就没找到一个是…