java进阶—一篇文章搞懂set 集合 及其底层实现

news/2024/12/13 4:34:47/

上节我们知道了List 下的两大 子类 ArrayList 跟 linkedList

ArrayList 数组结构 查询快,增删慢

LinkedList 链表结构 查询慢,增删快

来看看我们今天的主角: Set

Set 是 不可重复的,其底下也有两大子接口:

HashSet:无序且唯一

TreeSet :有序且唯一

先礼后兵,我们直接来看操作set集合有哪些方法,其实与list 的方法一样,两个子类都是,我们拿最常用的HashSet为例子

  • 添加(add)
  Set<String> hashSet = new HashSet<>();hashSet.add("java");hashSet.add("资讯");for (String item : hashSet) {System.out.println(item);}

在这里插入图片描述

  • 移除(remove)
Set<String> hashSet = new HashSet<>();hashSet.add("java");hashSet.add("公众号搜索:“java资讯");hashSet.remove("公众号搜索:“java资讯");for (String item : hashSet) {System.out.println(item);}

在这里插入图片描述

  • 判断集合中是否有该元素(contains)
Set<String> hashSet = new HashSet<>();hashSet.add("java");hashSet.add("资讯");boolean flag = hashSet.contains("java");System.out.println(flag);

在这里插入图片描述

  • 获取集合的大小(size)
Set<String> hashSet = new HashSet<>();hashSet.add("java");hashSet.add("资讯");int size = hashSet.size();System.out.println(size);

在这里插入图片描述

  • 判断集合是否为空(isEmpty)
Set<String> hashSet = new HashSet<>();hashSet.add("java");hashSet.add("资讯");boolean flag = hashSet.isEmpty();System.out.println(flag);

在这里插入图片描述

接下来我们一起看探索set 三个子类的底层 (面试会问,需了解,建议收藏,留个印象)

  • HashSet (无序且唯一)

HashSet 是Set 接口中最常见的实现类,底层数据结构是哈希表

什么是哈希表?

用一个例子来说明:

有这么24个篮球,编号分别为1-24,需要将篮球分成六组应该怎么分

这还不简单 :

编号 1 -4 第一组 5-8 第二组 9-12 第三组

编号 13-16 第四组 17-20 第五组 21-24 第六组

那如果我要找 16 号篮球球在哪个组呢? 这数据才24, 要找到也方便,要是数据量变大,成百上千,分成多个组,要快速找到想要的编号在哪个组,就显得困难了

这时候就推出了哈希,进行散列

具体实现:

分成6组

将 编号除 6 余数为0 的为 第零组:6、12、18、24

将 编号除 6 余数为1 的为 第一组:1、7、13、19

将 编号除 6 余数为2 的为 第二组:2、8、14、20

将 编号除 6 余数为3 的为 第三组:3、9、15、21

将 编号除 6 余数为4 的为 第四组:4、10、16、22

将 编号除 6 余数为5 的为 第五组:5、11、17、23

这要我们要找一个编号就很方便,比如找16,16%6 =4 16 在第四组 ,这种方式就是高效的散列,我们称之为Hash

来看看哈希的运行图解,还是以上述篮球分组为例:

在这里插入图片描述
这里有几个概念:

key:就是编号

索引:数组的下标,可以快速定位,检索,我们分组的序号

哈希函数:将编号映射到索引上,采用的是取余方法 % 余数代表数组下标

哈希桶:保存索引的值的数组或链表,每个索引相同的元素以链表形式连接

通过上述,可以知道,这个存放数据的散列表就是我们说的哈希表

现在我们来看看 HashSet 的无序且唯一这个特性,通过对比代码呈现

 public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("张三");list.add("李四");list.add("王五");list.add("王五");for (String item : list) {System.out.println(item);}System.out.println("-------------------------");Set<String> hashSet = new HashSet<>();hashSet.add("张三");hashSet.add("李四");hashSet.add("王五");hashSet.add("王五");for (String item : hashSet) {System.out.println(item);}
}

输出结果

在这里插入图片描述
可以看出,List 是有序的,谁先添加,谁先输出,而set没有按照顺序来,

并且list可以存储重复的数据,set会自动去重

  • TreeSet (有序,唯一)

TreeSet 底层实现是红黑树,默认排序为从小到大。是有序的,

注意:这里的有序并不是list 的 先进先出的有序,他会进行自然排序 ,对数据进行排序后输出

   Set<Integer> treeSet = new TreeSet<>();treeSet.add(88);treeSet.add(66);treeSet.add(10);treeSet.add(3);treeSet.add(6);for (Integer item : treeSet) {System.out.println(item);}

在这里插入图片描述

红黑树,这边有一个演示地址,复制到浏览器进行打开,https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

在这里插入图片描述
第一次存储数据没有树根就创建树根,然后这个元素 就是树根

第二次存储元素先跟节点比较,

          当存储元素的值大于根节点的值,将元素存储在右边当存储元素的值小于根节点的值,将元素存储在左边当存储元素的值等于根节点的值,那就不存了,因为是唯一的 

TreeSet 有两个排序机制,体现在它的构造器上,分为:自然排序,比较器排序

可以看看他的构造器:

//构造一个新的空 自然顺序进行排序。
TreeSet()
// 指定比较器进行排序。
TreeSet(Comparator<? super E> comparator)

自然排序用的是Comparable 接口有一个 compareTo(Object o) 方法,两个元素为0 相同,不再重复存储,返回正数 表明 前数据 大于 后数据 ,负数则反之

比较器排序 用的是 compare(Object o1,Object o2),返回结果跟判断原则跟compareTo 一样

主要区别,构造上也能看到,一个对应空参创建TreeSet 一个对应有参创建TreeSet

扩展

linkedHashSet (这个是HashSet的子节点),它是有序且唯一,底层结构为链表加哈希表,链表保证了元素有序(这个有序是顺序,不是排序的大小),有序是因为它在节点处增加了前和后 (属性维护节点的前后添加顺序)

   Set<Integer> treeSet = new LinkedHashSet<>();treeSet.add(88);treeSet.add(66);treeSet.add(10);treeSet.add(3);treeSet.add(6);for (Integer item : treeSet) {System.out.println(item);}

在这里插入图片描述
在这里插入图片描述
java进阶—List

Java中的集合

二维数组详细解析

Java中的注释


http://www.ppmy.cn/news/21933.html

相关文章

《MFC编程》:MFC程序执行流程

《MFC编程》&#xff1a;MFC程序启动《MFC编程》&#xff1a;MFC程序启动入口函数执行流程CWinApp的成员视频链接《MFC编程》&#xff1a;MFC程序启动 入口函数 MFC程序的入口函数与win32程序一样&#xff0c;都是从WinMain入口。 但是MFC库已经实现了WinMain函数&#xff0…

Leetcode.1145 二叉树着色游戏

题目链接 Leetcode.1145 二叉树着色游戏 Rating &#xff1a; 1741 题目描述 有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中&#xff0c;给出二叉树的根节点 root&#xff0c;树上总共有 n个节点&#xff0c;且 n为奇数&#xff0c;其中每个节点上的值从 1到 n各不相…

新手入门,深入解析 python lambda表达式

lambda 表达式是 Python 中的匿名函数。它接受任意数量的参数&#xff0c;并返回一个单个表达式的值。它的语法格式如下&#xff1a; lambda arguments: expression 文章目录lambda 函数原型解释lambda 函数用作其它参数lambda 函数高级的技巧多个参数返回多个值条件表达式嵌套…

我通过 tensorflow 预测了博客的粉丝数

前言&#xff1a;由于最近接触了 tensorflow.js&#xff0c;出于试一下的心态&#xff0c;想通过线性回归预测一下博客的粉丝走向和数量&#xff0c;结果翻车了。虽然场景用错地方&#xff0c;但是整个实战方法用在身高体重等方面的预测还是有可行性&#xff0c;所以就记录下来…

redis加锁的几种方法

1. redis加锁分类 redis能用的的加锁命令分表是INCR、SETNX、SET 2. 第一种锁命令INCR 这种加锁的思路是&#xff0c; key 不存在&#xff0c;那么 key 的值会先被初始化为 0 &#xff0c;然后再执行 INCR 操作进行加一。 然后其它用户在执行 INCR 操作进行加一时&#xff0c;…

WebRTC SDP协议--新属性

一 Plan B、Unified Plan Unified Plan&#xff1a;每路视频流都有一个mvideo的描述。比如&#xff1a;有2路视频&#xff0c;有2个mvideo。 Plan B&#xff1a;无论几路流&#xff0c;只有1个mvideo。 描述不了的情况&#xff1a;ssrc对应的编码格式不同&#xff0c;一个H26…

mysql global index_mysql_fullindex全文索引

MySQL 5.7.6 开始&#xff0c;引入了一个 ngram 全文分析器支持汉语无空格分隔符 事实上&#xff0c;MyISAM 存储引擎对全文索引的支持有很多的限制&#xff0c;例如表级别锁对性能的影响、数据文件的崩溃、崩溃后的恢复等&#xff0c;这使得 MyISAM 的全文索引对于很多的应用…

【C++】内联函数

【C】内联函数 文章目录【C】内联函数1、内联函数概念2、内联函数的特性2.1 空间与时间2.2 忽略的内联函数2.3 声明和定义3、总结1、内联函数概念 函数是一个可以重复使用的代码块&#xff0c;CPU 会一条一条地挨着执行其中的代码。CPU 在执行主调函数代码时如果遇到了被调函数…