当前位置: 首页 > news >正文

【从0开始学习Java | 第17篇】集合(中-Set部分)

在这里插入图片描述

文章目录

  • Java集合之Set:无序不重复的元素容器
    • 一、Set接口的核心特性
    • 二、常用实现类及底层原理
      • 1. HashSet:基于哈希表的高效实现
      • 2. LinkedHashSet:保留插入顺序的哈希实现
      • 3. TreeSet:基于红黑树的排序实现
    • 三、实现类对比与选择建议
    • 四、使用注意事项
    • 总结

Java集合之Set:无序不重复的元素容器

在Java集合框架中,Set是与List并列的重要接口,它以“无序且元素不重复”为核心特性,在数据去重、快速查找等场景中被广泛应用。本文将深入解析Set接口及其常用实现类,帮助你理解它们的底层原理与适用场景。

一、Set接口的核心特性

Set继承自Collection接口,但其行为与List有显著区别:

  • 无序性:元素的存储顺序与插入顺序无关(特殊实现类如LinkedHashSet除外),不能通过索引访问元素。
  • 唯一性/不重复:集合中不允许存在重复元素,判断重复的依据是equals()方法,若元素重写了equals(),通常需同时重写hashCode()以保证一致性。
  • 无索引:没有类似get(int index)的方法,遍历需通过迭代器(Iterator)或增强for循环。

Set接口的常用方法与Collection基本一致,如add()remove()contains()size()等,核心差异体现在实现类对“去重”和“查找效率”的不同优化上。

在这里插入图片描述

二、常用实现类及底层原理

1. HashSet:基于哈希表的高效实现

在这里插入图片描述

在这里插入图片描述1. JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
2. 如果集合中存储的是自定义对象,必须重写hashCodeequals方法才能用来比较属性值是否相同

在知道了HashSet的底层原理后,我们就可以回答以下三个问题了:

1. HashSet为什么存和取的顺序不一样?

因为HashSet可能是数组+链表+红黑树的结合体,而取的时候,是从最小的地址值开始遍历,而先放进去的元素的地址值不一定就小,以上都造成了HashSet的存取顺序不同。

2. HashSet为什么没有索引?

因为HashSet是数组+链表+红黑树的结合体,若存在索引,无法分配索引,难道同一个链表上和同一个红黑树上的元素都用同一个索引码,因此便取消了HashSet的索引。

3. HashSet是利用什么机制保证数据去重的?

首先利用 HashCode方法来确定元素的位置,再通过equals方法来判断属性值是否重复【重写了HashCode 和 equals 方法】。

HashSetSet最常用的实现类,底层通过哈希表(HashMap) 实现,其核心特性如下:

  • 存储原理:借助HashMapkey存储元素(value为固定常量),利用哈希表的特性保证元素唯一。
  • 去重逻辑:添加元素时,先通过hashCode()计算哈希值,若哈希值不同则直接存储;若哈希值相同,再通过equals()判断是否为同一元素,相同则拒绝添加。
  • 性能
    • add()、remove()、contains()等操作的平均时间复杂度为O(1),效率极高。
    • 无序性:元素存储位置由哈希值决定,与插入顺序无关。
  • 注意事项
    • 元素需重写hashCode()equals(),否则可能导致重复元素存入(如自定义对象未重写方法时,默认使用地址值判断【重要的事反复强调】)。
    • 线程不安全,多线程环境需手动同步(如使用Collections.synchronizedSet())。

示例代码

Set<String> hashSet = new HashSet<>();
hashSet.add("apple");
hashSet.add("banana");
hashSet.add("apple"); // 重复元素,添加失败
System.out.println(hashSet); // 输出可能为 [banana, apple](顺序不确定)

2. LinkedHashSet:保留插入顺序的哈希实现

在这里插入图片描述

LinkedHashSet继承自HashSet,底层通过LinkedHashMap实现,在哈希表基础上增加了双向链表,用于记录元素的插入顺序,因此具备以下特性:

  • 有序性:遍历元素时按插入顺序返回,解决了HashSet的无序问题。
  • 性能
    • 插入和删除效率略低于HashSet(因需维护链表),但遍历效率更高。
    • 时间复杂度仍为O(1)(平均),与HashSet接近。
  • 适用场景:需要去重且保留插入顺序的场景(如日志记录、历史操作跟踪)。

示例代码

Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("first");
linkedHashSet.add("second");
linkedHashSet.add("first"); // 去重
System.out.println(linkedHashSet); // 输出 [first, second](顺序与插入一致)

3. TreeSet:基于红黑树的排序实现

TreeSet底层通过红黑树(自平衡二叉查找树) 实现,核心特性是元素可排序

  • 两种排序方式
    • 自然排序:元素实现Comparable接口,重写compareTo()方法(如IntegerString默认支持)。

代码示例:

public class Student implements Comparable<Student> {private String name;private int age;@Overridepublic int compareTo(Student o) {//this:表示当前要添加的元素//o:表示已经在红黑树存在的元素return this.age - o.age; // 按年龄升序排列}
}

排序规则图示:

在这里插入图片描述

适用场景

  • 元素类有明确且唯一的默认排序逻辑(如数字按大小、字符串按字典序)。
  • 排序规则需要在多处复用,且不会频繁变更。
  • 元素类是自定义类,允许修改其代码以实现 Comparable 接口。

定制排序 / 比较器排序:创建TreeSet时传入Comparator对象,自定义排序逻辑。

// 按字符串长度排序
// lambda表达式版:
TreeSet<String> ts= new TreeSet<>((o1, o2) -> o1.length() - o2.length());// 原始版本:
TreeSet<String> ts= new TreeSet<>(new Comparator<String>() {@Override// o1:表示当前要添加的元素// o2:表示已经在红黑树存在的元素public int compare(String o1, String o2) {return o1.length() - o2.length();}
});

适用场景

  • 元素类无法修改(如 String、Integer 等 JDK 内置类),无法实现 Comparable
  • 需要临时改变排序规则(同一批元素在不同场景下按不同规则排序)。
  • 元素类已有默认排序,但需要覆盖默认规则(如数字默认升序,需临时按降序排列)。

  • 去重逻辑:通过排序规则判断元素是否重复(compareTo()返回0则视为重复,而非 equals() 方法,因为其底层为红黑树,非哈希表)。
  • 性能
    • add()、remove()、contains()操作的时间复杂度为O(log n),适合需要频繁排序和查找的场景。
    • 遍历元素时按排序顺序返回,无需额外排序操作。
  • 注意事项
    • 元素必须可比较(否则会抛出ClassCastException)。
    • 线程不安全,多线程环境需同步处理。

示例代码

// 自然排序(String默认按字典序)
Set<String> treeSet = new TreeSet<>();
treeSet.add("orange");
treeSet.add("apple");
treeSet.add("banana");
System.out.println(treeSet); // 输出 [apple, banana, orange](按字典序排序)// 定制排序(整数降序)
Set<Integer> customTreeSet = new TreeSet<>((a, b) -> b - a);
customTreeSet.add(3);
customTreeSet.add(1);
customTreeSet.add(2);
System.out.println(customTreeSet); // 输出 [3, 2, 1]

使用原则默认使用第一种,如果第一种不能满足当前需求,就使用第二种

三、实现类对比与选择建议

三者均不重复,且无索引哈希表 => 数组+单向链表+红黑树

实现类底层结构有序性去重依据平均时间复杂度适用场景
HashSet哈希表无序hashCode() + equals()O(1)高效去重,不关心顺序
LinkedHashSet哈希表+双向链表有序,插入顺序hashCode() + equals()O(1)去重且需保留插入顺序
TreeSet红黑树可排序compareTo()/ComparatorO(log n)去重且需排序或频繁范围查询

四、使用注意事项

  1. 元素的不可变性:若元素是可变对象,修改其属性可能导致哈希值或排序位置变化,引发Set内部状态混乱(如HashSet中元素修改后可能无法被正确查找)。建议使用不可变对象(如StringInteger)作为Set元素。

  2. hashCode()与equals()的一致性

    • a.equals(b) == true,则a.hashCode()必须等于b.hashCode()
    • 重写时尽量保证哈希值分布均匀(减少哈希冲突),避免影响HashSet性能。
  3. 线程安全:所有Set实现类均线程不安全,多线程并发修改时需使用Collections.synchronizedSet()包装,或直接使用ConcurrentHashMap(JDK 1.8+)的newKeySet()方法创建线程安全的Set

总结

HashSet追求高效,LinkedHashSet兼顾顺序,TreeSet专注排序。理解它们的底层原理(哈希表、红黑树)和特性差异,能帮助你在实际开发中选择最合适的容器,提升代码效率与可读性。


如果我的内容对你有帮助,请 点赞 评论 收藏 。创作不易,大家的支持就是我坚持下去的动力!
在这里插入图片描述


文章转载自:

http://7JygEP7w.wjtxt.cn
http://BqlL27bD.wjtxt.cn
http://xiVqyfn8.wjtxt.cn
http://y2VgcJP9.wjtxt.cn
http://8qmxlbkf.wjtxt.cn
http://2sr0IUjo.wjtxt.cn
http://5G73hIFW.wjtxt.cn
http://RsYGvN7n.wjtxt.cn
http://d27pikRu.wjtxt.cn
http://HxvQ9CJs.wjtxt.cn
http://TyWdZjmr.wjtxt.cn
http://qiIouQqb.wjtxt.cn
http://aw8CWygi.wjtxt.cn
http://iX2ErPAC.wjtxt.cn
http://hqA1rtB1.wjtxt.cn
http://KCpayBFK.wjtxt.cn
http://LFy1sMvz.wjtxt.cn
http://Du9Asc4f.wjtxt.cn
http://EreVgAS8.wjtxt.cn
http://TGYTWfDt.wjtxt.cn
http://MXCE0PRC.wjtxt.cn
http://bpcrLeTj.wjtxt.cn
http://zRll5UkE.wjtxt.cn
http://wmZagSks.wjtxt.cn
http://IaCHf5rH.wjtxt.cn
http://ZFbHAbug.wjtxt.cn
http://EyLVHOj0.wjtxt.cn
http://q5p8nJBr.wjtxt.cn
http://YIvywbWL.wjtxt.cn
http://yav8zAH4.wjtxt.cn
http://www.dtcms.com/a/377862.html

相关文章:

  • 【AI指导】Python实现prophet模型的业绩预测
  • RPA-4.0.0.0_SAAS新版本已上线,Edge扩展自动安装,快速实现RPA流程自动化
  • Server 13 ,CentOS 上使用 Nginx 部署多个前端项目完整指南( 支持多端口与脚本自动化 )
  • Java后端测试
  • Skywork-OR1:昆仑万维开源的数学代码推理系列模型
  • 【Linux】基本指令 · 上
  • OBS插件详细教程:OBS美颜插件下载,OBS美颜插件怎么用?
  • 如何在 Spring Boot 中指定不同的配置文件?
  • spring boot 拦截器增加语言信息
  • leedcode 算法刷题第三十二天
  • CentOS 7 下iscsi存储服务配置验证
  • 求解指定泛函的驻点所满足的偏微分方程及边界条件
  • 股指期货保证金一手需要多少钱?
  • LVS与Keepalived详解(一)负载均衡集群介绍
  • 【Proteus仿真】按键控制系列仿真——LED灯表示按键状态/按键控制LED灯/4*4矩阵键盘控制LED
  • 【前沿技术拓展Trip one】 芯片自动化和具身智能
  • javaEE之线程初步认识
  • `struct iovec`详解
  • python超市购物 2025年6月电子学会python编程等级考试一级真题答案解析
  • 项目模块划分
  • leetcode18(无重复字符的最长子串)
  • HackathonCTF: 1
  • redis cluster(去中心化)
  • 量子机器学习入门:三种数据编码方法对比与应用
  • 【Mysql】数据库的内置函数
  • 【Unity基础】枚举AudioType各个枚举项对应的音频文件类型
  • 2025数字化转型时代必备证书有哪些?
  • 认知-学习-时间管理系统模型-md说明文档
  • 如何用Postman做接口自动化测试
  • huggingface模型中各文件详解