news 2026/11/4 7:19:33

Set接口、HashSet、LinkedHashSet

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Set接口、HashSet、LinkedHashSet

课堂要点回顾:

  1. Set 是接口,继承 Collection,无索引、元素不能重复;不能使用普通 for 循环遍历
  2. HashSet 底层哈希表,无序去重;自定义类存入 HashSet 必须重写hashCode()、equals(),否则无法按业务属性去重
  3. LinkedHashSet 底层哈希表 + 双向链表,存取顺序一致、依旧去重,维护链表带来额外内存与性能开销
  4. 集合不能直接存基本数据类型,必须使用包装类;集合也可以存放自定义实体类
  5. 实体类要点:属性私有(封装)、get/set、构造方法、重写toString()用于对象打印输出
  6. Set 全部遍历方式:迭代器、增强 for、forEach (Lambda / 匿名内部类),和 List 通用

一、Set 接口

1. 特点

  1. 属于Collection子接口,没有索引,不能用普通 for 循环(依靠下标 get)遍历
  2. 元素不允许重复,自带去重能力
  3. 存取顺序是否一致取决于具体实现类(HashSet 无序;LinkedHashSet 存取有序;TreeSet 排序)
  4. 方法全部继承自Collection:add、remove、contains、size、clear、isEmpty()

课堂提示:List 可以get(索引)获取元素;Set 没有索引,没有 get 方法,想要查找元素只能遍历。

2. Set 的三种实现类

实现类底层结构特点
HashSet哈希表(数组 + 链表 + 红黑树 JDK8)无序、去重,读写速度快
LinkedHashSet哈希表 +双向链表存取顺序一致、去重;链表带来额外开销
TreeSet平衡二叉树(红黑树)元素大小排序、去重(下一期讲解)

3. Set 集合遍历(全部 3 种方式,和 List 通用)

Set<String> set = new HashSet<>(); set.add("张三"); set.add("李四"); //①迭代器 Iterator<String> it = set.iterator(); while(it.hasNext()){ System.out.println(it.next()); } //②增强for循环 for(String s : set){ System.out.println(s); } //③forEach,支持匿名内部类 / Lambda表达式(课堂讲到Consumer函数式接口) //匿名内部类写法 set.forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); //Lambda简写 set.forEach(s -> System.out.println(s)); //方法引用简写 set.forEach(System.out::println);

课堂重点:forEach 底层使用Consumer函数式接口,接口中只有一个accept()抽象方法,接收参数无返回值。

二、HashSet

1. 底层与特点

  • 底层哈希表,存取顺序无序,不保证存入和取出顺序一致
  • 可以存入null,最多允许一个 null
  • 线程不安全;增删查询效率很高
  • 默认初始容量 16,加载因子 0.75,达到阈值自动扩容为原来 2 倍;链表长度≥8 且数组≥64 转为红黑树

2. add 添加元素、去重完整流程(课堂讲解 “桶” 机制)

  1. 调用对象hashCode()得到哈希值,通过哈希值计算数组存储位置(桶的位置)
  2. 如果该桶位置没有元素 → 直接存入
  3. 如果桶位置已经存在元素(哈希冲突):
    • 比较两个对象哈希值;哈希值不同:挂到链表后面
    • 哈希值相同,调用equals()做内容比较
      • equals=true:判定重复元素,舍弃,不存入(实现去重)
      • equals=false:不是重复,挂到链表节点后面

⚠️课堂强调:HashSet 去重依赖hashCode() + equals()两个方法同时生效,缺一不可JDK 自带类(String、Integer)已经重写好这两个方法,可以直接去重;自定义实体类不重写,则按照对象地址比较,业务上无法实现去重!

3. 自定义实体类存入 HashSet(课堂 Student 实体案例)

实体类编写规范(封装思想):

  1. 成员变量private 私有,保护数据安全
  2. 提供无参、有参构造方法
  3. 根据业务需要编写 get、set 方法,不需要就不写,不是必须全部生成
  4. 重写toString():打印对象时输出属性内容,否则打印类名@内存地址
  5. 如果需要 HashSet 按业务属性去重:必须重写hashCode()和equals()
import java.util.Objects; //实体类,对应现实业务实体,未来可以映射数据库表 class Student{ private Integer id; private String name; //无参构造 public Student() {} //有参构造 public Student(Integer id, String name) { this.id = id; this.name = name; } //get、set public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } //重写toString:输出对象时展示属性,而不是内存地址 @Override public String toString() { return "Student{id=" + id + ", name='" + name + "'}"; } //重写hashCode + equals:业务规则:id相同就视为同一个学生,做去重 @Override public boolean equals(Object o) { //1.同一个对象(地址完全相同)直接true if (this == o) return true; //2.判断对象是否为null,判断类型是否一致 if (o == null || getClass() != o.getClass()) return false; //向下转型 Student student = (Student) o; //业务规则:只比较学号id return Objects.equals(id, student.id); } @Override public int hashCode() { //只使用id计算哈希值 return Objects.hash(id); } }

测试代码:

HashSet<Student> stuSet = new HashSet<>(); stuSet.add(new Student(1001,"小明")); stuSet.add(new Student(1001,"小明二号")); //id重复,会被去重丢弃 stuSet.add(new Student(1002,"小红")); stuSet.forEach(System.out::println);

这里只对比 id;哪怕 name 名字不一样,只要 id 相同就判定重复。重写 equals、hashCode 时根据业务需求确定比较哪些属性。

三、LinkedHashSet

1. 底层和特点

底层:哈希表 + 双向链表

  1. 依旧元素不可重复,依靠 hashCode+equals 去重
  2. 双向链表记录元素添加顺序,存取顺序保持一致
  3. 缺点:需要维护双向链表,占用更多内存;增删操作会有额外性能开销
LinkedHashSet<String> lhs = new LinkedHashSet<>(); lhs.add("A"); lhs.add("B"); lhs.add("C"); //遍历输出顺序:A B C 和添加顺序完全一致 lhs.forEach(System.out::println);

2. HashSet 与 LinkedHashSet 对比(课堂总结)

项目HashSetLinkedHashSet
底层哈希表(数组 + 链表 + 红黑树)哈希表 + 双向链表
存取顺序无序,不保证顺序存取顺序保持一致
性能增删查询快,开销小性能略低,维护链表有开销
内存占用较小更大,需要存储双向链表指针
适用场景只需要去重,不关心顺序(开发最常用)需要保留元素存入顺序同时做去重

课堂建议:没有顺序需求优先使用HashSet;需要保证存入顺序再选LinkedHashSet。

四、课堂高频易错汇总

  1. Set 没有索引,没有 get (int index) 方法,遍历只能迭代器、增强 for、forEach;不能普通 for 下标遍历。
  2. 集合不能存储基本类型,必须使用包装类,如HashSet<Integer>,不能写HashSet<int>。
  3. 自定义类存入 HashSet 想要业务去重,必须重写hashCode()和equals();只重写其中一个,去重会失效。
  4. toString()不重写,打印对象输出内存地址;重写后打印对象属性,方便调试输出。
  5. LinkedHashSet 有序≠排序!只是记录添加的先后顺序;想要按大小排序用 TreeSet。
  6. 遍历 Set 的时候,不要直接用集合 add/remove 修改集合,会触发并发修改异常;需要删除用迭代器it.remove()。

五、速记口诀

Set 接口无索引,元素重复不允许;

HashSet 哈希表,无序去重效率好;

自定义类存集合,hashCode equals 不能少;

LinkedHash 加链表,存入取出顺序保;

链表维护有开销,需要顺序才去找;

实体类要封装好,toString 打印巧;

遍历三种方式记,迭代增强 forEach。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 7:18:51

EtherCAT学习之路 —— 概述(2)

接前一篇文章&#xff1a;EtherCAT学习之路 —— 概述&#xff08;1&#xff09; 3. EtherCAT系统组成 EtherCAT系统主要就一个主站和若干从站组成。如图3-1所示&#xff1a; 图3-1 EtherCAT系统组成标题 EtherCAT一般使用软件的方式来实现主站&#xff0c;包括倍福的TwinCAT…

作者头像 李华
网站建设 2026/11/4 7:18:11

如何把科研论文写清楚?从 Cormac McCarthy 的建议到可执行的修改方法

本文导读&#xff1a;论文写作不只是检查语法&#xff0c;还要让读者迅速理解研究问题、方法、证据和结论。本文根据 Nature 刊登的 Cormac McCarthy 科研写作建议&#xff0c;结合计算机与工程领域的示例&#xff0c;介绍如何确定论文主线、组织段落、精简句子、解释公式&…

作者头像 李华
网站建设 2026/11/4 7:17:46

计算机系统-浮点数(四)

一、浮点数当机器字长为n时&#xff0c;定点数的补码和移码可表示2ⁿ个数&#xff0c;而其原码和反码只能表示2ⁿ-1个数&#xff08;0的表示占用了两个编码&#xff09;&#xff0c;因此&#xff0c;定点数所能表示的数值范围比较小&#xff0c;在运算中很容易因结果超出范围而…

作者头像 李华
网站建设 2026/11/4 7:16:26

工业智能体如何用好“老师傅经验”?从工业RAG到设备运维闭环

设备出现报警后&#xff0c;工程师往往需要先查设备手册&#xff0c;再翻维修工单&#xff0c;联系熟悉这台设备的同事&#xff0c;最后结合运行参数判断问题。资料并不少&#xff0c;但完成一次有效排查&#xff0c;依然要跨越多个系统和知识断点。这也是工业智能体落地时需要…

作者头像 李华