首页 > 其他分享 >Set 系列集合的深入理解与应用

Set 系列集合的深入理解与应用

时间:2025-01-12 18:29:45浏览次数:3  
标签:Set 系列 元素 添加 哈希 集合 排序

在 Java 中,Set 系列集合是一个非常重要的集合框架,它提供了多种实现,每个实现都具有独特的特性,适用于不同的场景。本文将详细介绍 Set 系列集合的相关知识,包括其通用特性、各种实现类及其底层原理。

一、Set 集合的通用特性

Set 集合具有以下几个显著的特点:

  • 无序:这意味着元素的存储顺序和取出顺序可能不一致。当你将元素添加到 Set 集合中时,它们不会按照添加的顺序被存储,在取出时,其顺序也会有所不同。
  • 不重复:Set 集合的一个重要特性是它能够自动去除重复元素。当你尝试添加一个已经存在于集合中的元素时,添加操作将不会成功。
  • 无索引:Set 集合没有提供带索引的方法,因此你不能使用普通的 for 循环遍历,也不能通过索引来获取元素。

二、Set 集合的实现类

HashSet

  • 特点:无序、不重复、无索引。
  • 底层原理
    • HashSet 底层集合采用哈希表存储数据,哈希表对于增删改查数据性能都比较好。
    • 具体实现步骤如下:
      1. 创建一个默认长度为 16,默认加载因子为 0.75 的数组,数组名 table
      2. 根据元素的哈希值跟数组长度计算出应存入的位置,公式为 int index = (数组长度 - 1) & 哈希值
      3. 判断当前位置是否为 null,如果是 null 直接存入。
      4. 如果位置不是 null,表示有元素,则调用 equals 方法比较属性值。
      5. 若元素的属性相同:不存储;若元素的属性不同:存入数组形成链表。
       
      • JDK8 以前:新元素存入数组,老元素挂在新元素下面。
      • JDK8 以后:新元素直接挂在老元素下面。
      • JDK8 以后,当链表长度超过 8,而且数组长度大于等于 64 时,自动转换为红黑树。
    • 哈希表组成
      • JDK8 之前:数组 + 链表。
      • JDK8 开始:数组 + 链表 + 红黑树。
    • 哈希值
      • 哈希值是对象的整数表现形式,根据 hashCode 方法计算出来的 int 类型的整数。
      • 该方法定义在 Object 类中,所有对象都可以调用,默认使用地址值进行计算。
      • 一般情况下,会重写 hashCode 方法,利用对象内部属性值计算哈希值。
    • 对象的哈希值特点
      • 如果没有重写 hashCode 方法,不同对象计算出的哈希值是不同的。
      • 如果已经重写 hashCode 方法,不同的对象只要属性值相同,计算出的哈希值就是一样的。
      • 在小部分的情况下,不同属性值或者不同地址值计算出来的哈希值也有可能一样(哈希碰撞)。

LinkedHashSet

  • 特点:有序、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致。
  • 底层原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序。

TreeSet

  • 特点:不重复、无索引、可排序。
  • 可排序原理:TreeSet 集合底层基于红黑树的数据结构实现排序,因此在增删改查性能上表现较好。
  • 排序规则
    • 对于数据类型:如 IntegerDouble,默认按照从小到大的顺序进行排序。
    • 对于字符、字符串类型:按照字符在 ASCII 码表中的数字升序排序。
    • 两种比较方式
      1. 默认排序 / 自然排序:Javabean 类实现 Comparable 接口指定比较规则。
@Override
public int compareTo(Student o) {
    return this.getAge() - o.getAge();
}
/*
this:表示当前要添加的元素
o:表示已经在红黑树存在的元素
返回值:
    负数:认为要添加的元素是小,存左边
    正数:认为要添加的元素是大,存右边
    0:认为要添加的元素已经存在,舍弃
*/
  1. 比较器排序:创建 TreeSet 对象时,传递比较器 Comparator。以下是一个使用 Lambda 表达式的示例:
public static void main(String[] args) {
    // 长度排序如果长度一样则按照首字母排序
    // o1 表示:当前要添加的元素
    // o2 表示:已经在红黑树存在的元素
    TreeSet<String> ts = new TreeSet<>((o1, o2) -> {
        int i = o1.length() - o2.length();
        i = i == 0? o1.compareTo(o2) : i;
        return i;
    });
    ts.add("c");
    ts.add("cffffffffffff");
    ts.add("bu");
    ts.add("cfm");
    System.out.println(ts);
}

三、Set 集合的使用示例

以下是一个使用 Set 系列集合的简单示例,展示了如何添加元素以及使用不同的遍历方式:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        // 1. 创建一个 Set 集合的对象
        Set<String> s = new HashSet<>();

        // 2. 添加元素
        // 如果当前元素是第一次增加,那么可以添加成功,返回 true
        // 如果当前元素是第二次添加,那么添加失败,返回 false
        s.add("张三");
        s.add("张三");
        s.add("王五");
        s.add("李四");

        // 3. 打印集合
        // 无序
        // System.out.println(s); // [李四, 张三, 王五]

        // 迭代器遍历
        Iterator<String> it = s.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }

        // 增强 for 循环遍历
        for (String str : s) {
            System.out.println(str);
        }

        // Lambda 表达式遍历
        s.forEach(str -> System.out.println(str));
    }
}

在上述代码中,我们首先创建了一个 HashSet 对象,然后添加了一些元素。需要注意的是,由于 Set 集合不允许重复元素,当添加重复元素时,第二次添加操作将不会成功。

我们还展示了三种不同的遍历方式:

  • 迭代器遍历:通过 iterator() 方法获取迭代器,使用 hasNext() 方法判断是否还有元素,使用 next() 方法获取下一个元素。
  • 增强 for 循环遍历:这是一种简洁的遍历方式,适用于遍历实现了 Iterable 接口的集合。
  • Lambda 表达式遍历:使用 forEach() 方法和 Lambda 表达式,可以更加简洁地实现元素的遍历。

四、总结

  • HashSet:是一个通用的 Set 实现,当不需要保证元素的存储和取出顺序,且需要高效的元素添加、删除和查找操作时,使用 HashSet 是一个不错的选择。但如果存储自定义对象,务必重写 hashCode 和 equals 方法。
  • LinkedHashSet:当需要保证元素的存储和取出顺序,同时需要元素去重时,LinkedHashSet 是一个很好的选择,它在保证元素顺序方面具有优势。
  • TreeSet:当需要对元素进行排序时,可使用 TreeSet。根据不同的需求,可以使用自然排序(通过实现 Comparable 接口)或比较器排序(通过传递 Comparator)来定义元素的排序规则。

以上就是对 Set 系列集合的全面介绍,通过理解其特性和底层原理,我们可以根据具体的需求选择合适的 Set 实现类,并正确地使用它们。

标签:Set,系列,元素,添加,哈希,集合,排序
From: https://blog.csdn.net/2201_75813105/article/details/145096944

相关文章

  • JAVA之集合
    1、集合集合可以存储引用数据类型;集合不可以存储基本数据类型,若要存储,需封装成包装类;2、集合和数组的对比长度【数组长度固定,集合长度可变】存储类型【数组可以存基本数据类型和引用数据类型,集合可以存引用数据类型,若存储基本数据类型,需封装成包装类】3、ArrayList【打......
  • Redis 是一个开源的高性能键值对存储数据库,通常被用作缓存、消息队列和持久化数据库。
    Redis服务器是什么?Redis是一个开源的高性能键值对存储数据库,通常被用作缓存、消息队列和持久化数据库。Redis支持多种数据结构,如字符串、哈希、列表、集合、有序集合、位图等。它被广泛用于需要快速读写操作、低延迟的场景。Redis可以作为一个独立的数据库使用,也可以作为缓......
  • idea汉化教程,jetbrains系列软件都适用
    idea汉化教程,jetbrains系列软件都适用下载安装idea参考:pyhton、miniConda、pycharm下载安装配置中的pycharm下载安装配置即可,jetbrains系列软件都可如此操作。汉化打开安装好的idea,ctrl+alt+s或手动打开Settings/设置,点击左侧Plugins/插件页签,点击右侧Marketplace页签,在......
  • 【DMSQL系列】 达梦数据库写文件的方式探索
    前沿这篇文章整体算是......
  • 宇航用VIRTEX5系列FPGA的动态刷新方法及实现
    SRAM型FPGA在宇航领域有广泛的应用,为解决FPGA在空间环境中的单粒子翻转问题,增强设计的可靠性,本文介绍一种低成本的抗辐照解决方案。该方案从外置高可靠存储器中读取配置数据,通过定时刷新结合三模冗余的方式消除单粒子影响,提高系统的鲁棒性。    1.总体设计    ......
  • 自动化运维脚本编写规范是指在编写运维自动化脚本时,遵循的一系列最佳实践和标准。这些
    自动化运维脚本编写规范是指在编写运维自动化脚本时,遵循的一系列最佳实践和标准。这些规范确保脚本的可读性、可维护性、可靠性和一致性,同时减少出错的机会,并增强团队之间的协作效率。1. 是什么自动化运维脚本编写规范是为了确保脚本在自动化运维过程中能够高效、清晰、安全地......
  • Towards Better Multi-task Learning: A Framework for Optimizing Dataset Combinati
    本文是LLM系列文章,针对《TowardsBetterMulti-taskLearning:AFrameworkforOptimizingDatasetCombinationsinLargeLanguageModels》的翻译。迈向更好的多任务学习:一个优化大型语言模型中数据集组合的框架摘要1引言2相关工作3框架4实验设置5结果6......
  • 请说说setData的操作过程
    setData的操作过程在前端开发中,特别是在小程序开发中,扮演着至关重要的角色。它是将数据从逻辑层发送到视图层并进行更新的关键步骤。以下是setData操作过程的详细解释:一、准备数据在逻辑层中,开发者需要准备好要更新的数据。这些数据通常以对象的形式存在,其中键(key)对应着要更新的......
  • 错误修改系列---基于RNN模型的心脏病预测(pytorch实现)
    前言前几天发布了pytorch实现,TensorFlow实现为:基于RNN模型的心脏病预测(tensorflow实现),但是一处繁琐地方+一处错误,这篇文章进行修改,修改效果还是好了不少;源文章为:基于RNN模型的心脏病预测,提供tensorflow和pytorch实现错误一这个也不算是错误,就是之前数据标准化、划分......
  • JNI原生基础类型与集合类型认识
    1.JNI基础类型认识:JNI类型与C++类型及JAVA类型的对应关系  2.JNI基础类型对应的JAVA类型3.基础类型使用示例jboolean_jbool=false;//uint8_t%u无符号8位整数jbyte_byte=0xff;//int8_t;%d有符号8位整数jchar_char=0xffff;//uint16_t;%u无符号......