Java TreeSet 全面详解

目录

一、核心定位

二、核心特性

三、两种排序规则(核心重点)

[1. 自然排序(默认排序)](#1. 自然排序(默认排序))

适用场景

核心原理

[示例:自然排序(Integer 类型,默认升序)](#示例:自然排序(Integer 类型,默认升序))

自定义类实现自然排序

[2. 定制排序(显式指定排序规则)](#2. 定制排序(显式指定排序规则))

适用场景

核心原理

[示例:定制排序(Lambda 表达式简化)](#示例:定制排序(Lambda 表达式简化))

四、构造方法

示例:集合初始化构造

[五、常用 API(核心操作)](#五、常用 API(核心操作))

[1. 新增操作](#1. 新增操作)

[2. 查询操作(有序专属核心)](#2. 查询操作(有序专属核心))

[3. 删除操作](#3. 删除操作)

[4. 遍历操作](#4. 遍历操作)

[示例:常用 API 综合使用](#示例:常用 API 综合使用)

六、关键注意事项(避坑指南)

七、总结


你希望深入掌握 Java 中TreeSet的核心特性、底层实现、排序规则、常用 API 及使用场景,我会从基础到进阶逐步拆解,结合可运行示例帮你彻底理解这个有序无重复的集合。

一、核心定位

TreeSet 是 Java 集合框架中 SortedSet 接口的唯一实现类(基于红黑树) ,隶属于 java.util 包,核心特性是元素有序且无重复,适用于需要对集合元素进行排序和去重的场景(如排行榜、有序字典等)。

二、核心特性

  1. 底层实现 :基于 TreeMap 实现(底层是红黑树,一种自平衡的二叉查找树),TreeSet 的元素实际存储在 TreeMapkey 位置,value 固定为一个静态常量 PRESENTnew Object()),利用 TreeMap 的键唯一性实现去重,利用红黑树结构实现有序。
  2. 有序性 :元素并非按插入顺序排列,而是按指定排序规则进行排序(支持两种排序方式:自然排序 + 定制排序),排序后的数据结构为有序集合(升序默认,可通过定制排序改为降序)。
  3. 无重复性 :通过排序规则判断元素是否重复,而非 equals() 方法(若两个元素通过排序规则比较返回 0,则视为重复元素,无法加入 TreeSet)。
  4. 非线程安全 :多线程环境下并发修改 TreeSet(如同时添加 / 删除元素)会导致数据不一致,需手动保证线程安全(如使用 Collections.synchronizedSortedSet(new TreeSet<>()))。
  5. 不支持 null 元素(默认) :自然排序下,TreeSet 不允许添加 null 元素(会抛出 NullPointerException);定制排序中可手动支持 null,但不推荐(易引发逻辑混乱)。
  6. 时间复杂度 :基于红黑树的特性,增、删、查(包含 / 获取)操作的时间复杂度均为 O(log n)(优于 HashSetO(1),但需付出排序成本)。
  7. 元素不可修改(影响排序的字段) :若自定义对象存入 TreeSet 后,修改了影响排序规则的字段,会导致元素在红黑树中的位置失效,无法正确查找、删除或排序(避坑关键)。

三、两种排序规则(核心重点)

TreeSet 的有序性依赖排序规则,提供两种排序方式,不可同时使用(优先使用定制排序)

1. 自然排序(默认排序)

适用场景

元素类型本身实现了 java.lang.Comparable 接口(JDK 自带类如 StringIntegerLong 等均已实现该接口),默认按升序排列。

核心原理

元素通过 Comparable 接口的 int compareTo(T o) 方法进行比较:

  • 返回正数:当前元素 > 目标元素
  • 返回 0:当前元素 = 目标元素(视为重复,无法添加)
  • 返回负数:当前元素 < 目标元素
示例:自然排序(Integer 类型,默认升序)
复制代码
import java.util.TreeSet;

public class TreeSetNaturalSortDemo {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();

        // 乱序添加元素
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(5);
        treeSet.add(2);
        treeSet.add(3); // 重复元素,无法添加

        // 遍历:输出有序(升序)
        System.out.println("自然排序(升序):" + treeSet); // 输出:[1, 2, 3, 5]

        // 验证有序性
        System.out.println("第一个元素(最小):" + treeSet.first()); // 1
        System.out.println("最后一个元素(最大):" + treeSet.last()); // 5
    }
}
自定义类实现自然排序

若自定义类需使用自然排序,必须实现 Comparable 接口并重写 compareTo 方法:

复制代码
import java.util.TreeSet;

// 自定义用户类,按年龄升序自然排序
class User implements Comparable<User> {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写compareTo方法:按年龄升序排序,年龄相同视为重复
    @Override
    public int compareTo(User o) {
        return this.age - o.age; // 升序(o.age - this.age 为降序)
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

public class TreeSetCustomNaturalSort {
    public static void main(String[] args) {
        TreeSet<User> userSet = new TreeSet<>();
        userSet.add(new User("张三", 25));
        userSet.add(new User("李四", 22));
        userSet.add(new User("王五", 25)); // 年龄与张三相同,视为重复,无法添加
        userSet.add(new User("赵六", 30));

        // 遍历:按年龄升序排列
        System.out.println("自定义类自然排序:");
        userSet.forEach(System.out::println);
        // 输出:
        // User{name='李四', age=22}
        // User{name='张三', age=25}
        // User{name='赵六', age=30}
    }
}

2. 定制排序(显式指定排序规则)

适用场景
  • 元素类型未实现 Comparable 接口(避免修改原有类结构);
  • 不满足自然排序的规则(如需要降序排列、按自定义字段复杂排序)。
核心原理

创建 TreeSet 时,通过构造方法传入 java.util.Comparator 接口的实现类(匿名内部类 / Lambda 表达式),重写 int compare(T o1, T o2) 方法自定义排序规则:

  • 返回正数:o1 > o2
  • 返回 0:o1 = o2(视为重复元素,无法添加)
  • 返回负数:o1 < o2
示例:定制排序(Lambda 表达式简化)
复制代码
import java.util.Comparator;
import java.util.TreeSet;

// 自定义商品类(未实现Comparable接口)
class Goods {
    private String name;
    private double price;

    public Goods(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{name='" + name + "', price=" + price + "}";
    }
}

public class TreeSetCustomSortDemo {
    public static void main(String[] args) {
        // 1. 定制排序:按商品价格降序排列(Lambda 表达式简化 Comparator)
        TreeSet<Goods> goodsSet = new TreeSet<>((o1, o2) -> {
            // 价格降序:o2.price - o1.price(升序则反之)
            return Double.compare(o2.getPrice(), o1.getPrice());
        });

        // 2. 添加元素
        goodsSet.add(new Goods("手机", 5999.99));
        goodsSet.add(new Goods("电脑", 9999.99));
        goodsSet.add(new Goods("平板", 3999.99));
        goodsSet.add(new Goods("手表", 5999.99)); // 价格与手机相同,视为重复,无法添加

        // 3. 遍历:按价格降序排列
        System.out.println("商品价格降序排列:");
        goodsSet.forEach(System.out::println);
        // 输出:
        // Goods{name='电脑', price=9999.99}
        // Goods{name='手机', price=5999.99}
        // Goods{name='平板', price=3999.99}
    }
}

四、构造方法

TreeSet 提供 4 个构造方法,核心是指定排序规则或初始化元素:

构造方法签名 功能说明
TreeSet() 无参构造:默认采用自然排序 ,元素需实现 Comparable 接口
TreeSet(Comparator<? super E> comparator) 定制排序构造:传入 Comparator 实现类,自定义排序规则
TreeSet(Collection<? extends E> c) 集合初始化构造:将指定 Collection 元素转入 TreeSet,采用自然排序
TreeSet(SortedSet<E> s) 有序集合初始化构造:继承传入 SortedSet 的排序规则,快速创建同规则 TreeSet

示例:集合初始化构造

复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

public class TreeSetConstructorDemo {
    public static void main(String[] args) {
        // 1. 先创建一个乱序有重复的 List
        List<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(2);
        list.add(8);
        list.add(2);
        list.add(5);

        // 2. 通过 List 初始化 TreeSet(自然排序 + 去重)
        TreeSet<Integer> treeSet = new TreeSet<>(list);
        System.out.println("List 初始化 TreeSet:" + treeSet); // 输出:[2, 5, 8]
    }
}

五、常用 API(核心操作)

TreeSet 继承 SortedSet 接口,提供了普通集合的通用 API,还新增了有序集合专属的操作方法,核心 API 如下:

1. 新增操作

方法 功能说明
boolean add(E e) 添加单个元素(有序插入,重复元素返回 false
boolean addAll(Collection<? extends E> c) 添加指定集合的所有元素(自动去重 + 排序)

2. 查询操作(有序专属核心)

方法 功能说明
E first() 返回集合中最小元素(按排序规则)
E last() 返回集合中最大元素(按排序规则)
E ceiling(E e) 返回大于等于 e 的最小元素(无则返回 null
E floor(E e) 返回小于等于 e 的最大元素(无则返回 null
E higher(E e) 返回严格大于 e 的最小元素(无则返回 null,区别于 ceiling
E lower(E e) 返回严格小于 e 的最大元素(无则返回 null,区别于 floor
SortedSet<E> subSet(E fromElement, E toElement) 返回子集:包含 fromElement,不包含 toElement(遵循原排序规则)
SortedSet<E> headSet(E toElement) 返回头部子集:所有小于 toElement 的元素
SortedSet<E> tailSet(E fromElement) 返回尾部子集:所有大于等于 fromElement 的元素

3. 删除操作

方法 功能说明
boolean remove(Object o) 删除指定元素(成功返回 true,失败返回 false
E pollFirst() 删除并返回最小元素(集合为空返回 null
E pollLast() 删除并返回最大元素(集合为空返回 null
void clear() 清空集合所有元素

4. 遍历操作

支持 3 种常用遍历方式:迭代器、增强 for 循环、forEach (Java 8+)

示例:常用 API 综合使用

复制代码
import java.util.TreeSet;

public class TreeSetAPIDemo {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();
        // 1. 新增元素
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(5);
        treeSet.add(2);
        treeSet.add(4);
        System.out.println("初始化集合:" + treeSet); // [1, 2, 3, 4, 5]

        // 2. 查询操作
        System.out.println("最小元素:" + treeSet.first()); // 1
        System.out.println("最大元素:" + treeSet.last()); // 5
        System.out.println(">=3 的最小元素:" + treeSet.ceiling(3)); // 3
        System.out.println("<=3 的最大元素:" + treeSet.floor(3)); // 3
        System.out.println(">3 的最小元素:" + treeSet.higher(3)); // 4
        System.out.println("<3 的最大元素:" + treeSet.lower(3)); // 2
        System.out.println("子集 [2,4):" + treeSet.subSet(2, 4)); // [2, 3]
        System.out.println("头部子集 <4:" + treeSet.headSet(4)); // [1, 2, 3]
        System.out.println("尾部子集 >=3:" + treeSet.tailSet(3)); // [3, 4, 5]

        // 3. 删除操作
        treeSet.remove(3); // 删除元素3
        System.out.println("删除3后:" + treeSet); // [1, 2, 4, 5]
        System.out.println("删除最小元素:" + treeSet.pollFirst()); // 1
        System.out.println("删除最大元素:" + treeSet.pollLast()); // 5
        System.out.println("删除首尾后:" + treeSet); // [2, 4]

        // 4. 遍历操作
        System.out.println("增强 for 遍历:");
        for (Integer num : treeSet) {
            System.out.print(num + " "); // 2 4
        }
        System.out.println("\nforEach 遍历:");
        treeSet.forEach(num -> System.out.print(num + " ")); // 2 4
    }
}

六、关键注意事项(避坑指南)

  1. 排序规则一致性
  • 自定义类的排序规则(compareTo/compare 方法)应与 equals() 方法保持一致(即排序返回 0 时,equals() 应返回 true),否则会出现「元素去重但 equals 判断不相等」的逻辑矛盾。
  • 若存入 TreeSet 的自定义对象修改了影响排序的字段 (如 User 的 age 字段),会导致元素在红黑树中的位置失效,无法正确查找、删除该元素(推荐将排序字段设为 final,禁止修改)。
  1. 元素类型限制
  • 自然排序下,元素必须实现 Comparable 接口,否则添加元素时会抛出 ClassCastException
  • 不能添加不同类型的元素(如同时添加 IntegerString),否则会抛出 ClassCastException(无法比较不同类型)。
  1. null 元素限制
  • 自然排序:不支持 null 元素(compareTo 方法会抛出 NullPointerException)。

  • 定制排序:可手动支持 null(需在 compare 方法中先判断 null,避免空指针),示例:

    复制代码
    TreeSet<String> set = new TreeSet<>((o1, o2) -> {
        if (o1 == null) return -1; // null 排最前面
        if (o2 == null) return 1;
        return o1.compareTo(o2);
    });
    set.add(null);
    set.add("Java");
    System.out.println(set); // [null, Java]
  1. 线程安全问题
  • TreeSet 是非线程安全的,多线程并发修改(如 add + remove)会导致数据不一致。

  • 解决方案:使用 Collections.synchronizedSortedSet 包装 TreeSet,示例:

    复制代码
    import java.util.Collections;
    import java.util.SortedSet;
    import java.util.TreeSet;
    
    public class TreeSetThreadSafeDemo {
        public static void main(String[] args) {
            // 线程安全的 TreeSet
            SortedSet<Integer> safeTreeSet = Collections.synchronizedSortedSet(new TreeSet<>());
            safeTreeSet.add(1);
            safeTreeSet.add(2);
        }
    }
  1. 与 HashSet 的核心区别| 特性 | TreeSet | HashSet ||---------------------|------------------------|------------------------|| 底层实现 | TreeMap(红黑树) | HashMap(哈希表) || 有序性 | 有序(自然 / 定制排序) | 无序(插入顺序不保证) || 唯一性判断 | 排序规则(compareTo/compare) | equals () + hashCode () || 时间复杂度 | 增删查 O (log n) | 增删查 O (1)(哈希无冲突) || null 支持 | 自然排序不支持,定制排序可支持 | 支持(仅允许一个 null) || 适用场景 | 需要有序去重的场景 | 无需有序,追求高性能去重 |

七、总结

  1. 核心特性:有序(自然 / 定制排序)、无重复、基于红黑树、O (log n) 时间复杂度、非线程安全、不默认支持 null。
  2. 排序规则 :自然排序(元素实现 Comparable)、定制排序(传入 Comparator),重复元素判断依赖排序规则返回 0。
  3. 核心 APIfirst()/last()(首尾元素)、ceiling()/floor()(边界元素)、pollFirst()/pollLast()(删除首尾)、subSet()(子集)是有序场景的核心操作。
  4. 避坑要点 :排序字段不可修改、排序规则与 equals 一致、线程安全需包装、避免添加不同类型元素。
  5. 适用场景:排行榜(按分数排序)、有序字典(按关键字排序)、范围查询(如查询 [10, 20] 之间的元素)等需要有序去重的场景。
相关推荐
技术管理修行1 年前
Java核心技术【十八】Java集合框架精讲:List、Set、Map
map·hashmap·arraylist·hashset·treeset·treemap·linkedlist
牛马程序员‍2 年前
学习JavaEE日子 Day24 TreeSet,内置比较器,外置比较器,HashMap
hashmap·treeset·内置比较器·外置比较器
谢小涛2 年前
java stream distinct根据list某个字段去重
java·stream·distinct·treeset·list去重
爱敲代码的菜菜2 年前
【数据结构】搜索树&Map&Set
hashmap·搜索树·hashset·treeset·treemap