☰
TreeSet
2026/11/4 7:19:35 网站建设 项目流程

TreeSet 属于 Set 接口实现类,自带去重 + 自动排序;和 HashSet、LinkedHashSet 去重机制完全不一样,不依赖 hashCode () 和 equals (),依靠比较规则判定重复。 两种排序方案:自然排序(Comparable)、比较器排序(Comparator)

一、TreeSet 基础特点

  1. 底层:红黑树(平衡二叉搜索树)
  2. 两大核心特性:✅ 元素不重复(去重)、✅ 元素自动排序
  3. 无索引,不能用普通 for 循环遍历;遍历方式同样是迭代器、增强 for、forEach (Lambda)
  4. 不能存入 null,会抛出空指针异常(和 HashSet、LinkedHashSet 区分)
  5. 线程不安全
  6. 排序规则二选一:
    • 自然排序:元素类实现Comparable接口(默认方式)
    • 比较器排序:构造 TreeSet 时传入Comparator比较器(优先级高于自然排序)

课堂重点区分:

  • HashSet:无序,哈希表,靠 hashCode+equals 去重
  • LinkedHashSet:存取有序,哈希表 + 双向链表,靠 hashCode+equals 去重
  • TreeSet:大小排序,红黑树,靠比较方法返回值去重

判定重复的规则(必考)

调用比较方法,返回 0 → 判定为重复元素,不存入集合

  • 返回正数:当前元素大,放到右子树
  • 返回负数:当前元素小,放到左子树
  • 返回 0:相等,去重舍弃

二、方式 1:自然排序 Comparable(默认)

使用无参构造new TreeSet<>(),要求存入的实体类实现Comparable<E>接口,重写compareTo()方法。 JDK 自带类:Integer、String 已经实现 Comparable,可以直接放进 TreeSet; 自定义类(Student)必须手动实现 Comparable,否则直接抛 ClassCastException 类型转换异常

示例:Student 实体类,按照年龄升序排序,年龄相同按 id 升序

import java.util.Objects; // 实现Comparable接口,指定比较类型 class Student implements Comparable<Student>{ private Integer id; private String name; private int age; public Student() {} public Student(Integer id, String name, int age) { this.id = id; this.name = name; this.age = age; } // 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; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{id=" + id + ", name='" + name + "', age=" + age + "}"; } // 核心:重写compareTo,定义排序规则 @Override public int compareTo(Student o) { // 规则:优先按年龄升序 int res = this.age - o.age; // 如果年龄相等,再比较id,id升序 res = res == 0 ? this.id - o.id : res; return res; // 【降序写法】 return o.age - this.age; } }

测试代码:

TreeSet<Student> treeSet = new TreeSet<>(); treeSet.add(new Student(1001,"张三",18)); treeSet.add(new Student(1003,"李四",16)); treeSet.add(new Student(1002,"王五",18)); // Lambda遍历 treeSet.forEach(System.out::println);

输出顺序:李四 (16) → 张三 (18) → 王五 (18)

课堂要点:如果只比较 age,age 相同返回 0,就会判定为同一个学生,直接去重;所以多字段比较是常用写法。

三、方式 2:比较器排序 Comparator(优先级更高)

使用带参构造new TreeSet<>(Comparator比较器)优势:不用修改实体类代码,灵活切换排序规则;同一个实体类可以写多种排序逻辑 支持:匿名内部类、Lambda 表达式(课堂重点,Lambda 简化写法)

// 1.匿名内部类写法 TreeSet<Student> tree1 = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { // 按年龄降序,年龄相同按名字排序 int res = s2.getAge() - s1.getAge(); res = res == 0 ? s1.getName().compareTo(s2.getName()) : res; return res; } }); // 2.Lambda简写(课堂推荐简洁写法) TreeSet<Student> tree2 = new TreeSet<>((s1,s2)->{ int res = s2.getAge() - s1.getAge(); res = res == 0 ? s1.getName().compareTo(s2.getName()) : res; return res; });

优先级:Comparator 比较器排序 > Comparable 自然排序,两个都写的时候,以 Comparator 为准。

四、三种 Set 实现类完整对比表

集合底层结构顺序特点去重依据能否存 null效率
HashSet哈希表 (数组 + 链表 + 红黑树)无序hashCode()+equals()✅ 最多 1 个读写最快
LinkedHashSet哈希表 + 双向链表存取有序(添加顺序)hashCode()+equals()✅ 最多 1 个中等,维护链表
TreeSet红黑树元素大小排序compareTo /compare 返回值❌ 不能存 null较慢,需要排序

五、TreeSet 高频易错点

  1. 存入自定义对象,没有指定排序规则 → 抛出ClassCastException
  2. 判定重复不靠 equals,比较方法返回 0 就直接去重,这点和 HashSet 完全不同,非常容易混淆
  3. 不能存入 null,会空指针;HashSet 可以存一个 null
  4. 排序不是 “存取顺序”,是数值、属性的大小顺序,不要和 LinkedHashSet 的有序搞混
  5. 比较器 Comparator 支持 Lambda,因为是函数式接口,符合之前 Lambda 的知识点
  6. 红黑树是平衡二叉树,保证查询、增删时间复杂度 O (logn),不会退化成链表

六、选择使用场景

  • 只需要去重、不在乎顺序:优先 HashSet
  • 需要去重,同时保留添加顺序:LinkedHashSet
  • 需要去重,并且自动按属性大小排序:TreeSet

七、速记口诀

TreeSet 红黑树,排序去重两不误; Comparable 自然排,实体类里重写 compareTo; Comparator 比较器,构造传入更灵活; 返回零值就去重,和 Hash 规则不一样; 空值不能往里放,优先比较器来做主。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询