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,一经查实,立即删除!

相关文章

【JVM】类的⽣命周期和类加载的过程

在Java中&#xff0c;类的生命周期和类加载过程是Java虚拟机&#xff08;JVM&#xff09;管理的核心部分。类的生命周期包括从类被加载到内存直到类被卸载的整个过程。类加载过程可以细分为多个阶段&#xff1a;加载、链接&#xff08;包括验证、准备、解析&#xff09;、初始化…

代码签名证书申请指南

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

Elasticsearch-ES查询单字段去重

ES 语句 整体数据 GET wkl_test/_search {"query": {"match_all": {}} }结果&#xff1a; {"took" : 123,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0…

R语言自定义vlookup函数

############################################################## #######自定义函数integrate_and_match_values用于提取数据 ############################################################# integrate_and_match_values <- function(target_data,target_id_col,target_…

(一)Kafka 安全之使用 SSL 的加密和身份验证

目录 一. 前言 二. 使用 SSL 的加密和身份验证 2.1. 为每个 Kafka Broker 生成 SSL 密钥和证书 2.1.1. 主机名验证&#xff08;Host Name Verification&#xff09; 2.1.2. 注意&#xff08;Note&#xff09; 一. 前言 SSL&#xff08;Secure Sockets Layer&#xff09;是…

Oracle数据库面试题-14

81. 解释RAC&#xff08;Real Application Clusters&#xff09;的工作原理。 RAC&#xff08;Real Application Clusters&#xff09;是Oracle Corporation开发的高可用性解决方案&#xff0c;它允许用户将多个数据库实例集群在一起&#xff0c;以提高数据库系统的可用性和性…

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

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

【人工智能】人工智能就业岗位发展方向有哪些?

人工智能领域的岗位多样&#xff0c;涵盖了从技术研发到应用实施、从产品设计到市场运营等各个方面&#xff0c;以下是人工智能就业岗位的主要发展方向 研发与应用岗位&#xff1a; 机器学习工程师&#xff1a;负责开发和实施机器学习算法&#xff0c;解决各种问题&#xff0c…

CDA二级(Level II)数据分析师——考试内容梳理二

逻辑回归&#xff1a;Ln(P/(1-P)-30.06X0.05X2-0.02X3X1 岭回归的扰动性越大&#xff0c;模型越不容易受到共线性的影响&#xff1b; LOSSO只是缓解了由于共线性导致的估计误差的问题&#xff0c;而不是解决共线性 &#xff1b; AUC值接近0.5时&#xff0c;我们认为这个模型…

ARM32开发--FreeRTOS-事件组

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

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

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

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

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

【postgresql初级使用】条件表达式触发器,兼顾DML执行性能,又能执行复杂逻辑,只在结帐时计算总帐

条件触发器 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#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…

Fontconfig head is null, check your fonts or fonts configuration问题解决

报错信息&#xff1a; Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [R equest processing failed: com.alibaba.excel.exception.ExcelGenerateException: java.lang.InternalError: java.lang.reflect.InvocationTargetExcep…