深入浅出 Java 有序集合:TreeSet 与 TreeMap实例详解
作者:我是一筐橙子
在Java中,TreeSet 和 TreeMap 都是基于红黑树实现的集合类,它们都实现了 NavigableSet 和 SortedMap 接口,提供了对元素的自然排序或自定义排序的支持,下面分别详细介绍这两个类,感兴趣的朋友一起看看吧
深入浅出 Java 有序集合:TreeSet 与 TreeMap 详解

在算法竞赛或高性能系统开发中,我们经常需要动态维护一组有序数据,例如实时排序、寻找最接近的数值或进行区间查询。在这种场景下,Java 集合框架中的 TreeSet 和 TreeMap 便是不可或缺的“神兵利器”。

一、核心 API:寻找“邻居”的专家
TreeSet 和 TreeMap 最强大的地方在于它们提供的 Navigable 导航方法,这些方法在处理区间问题(如 Range Module)时效率极高:
| 方法 | 功能描述 | 找不到时返回 |
|---|---|---|
lower(e) / lowerKey(k) | 找到严格小于目标的最大元素 | null |
floor(e) / floorKey(k) | 找到小于等于目标的最大元素 | null |
ceiling(e) / ceilingKey(k) | 找到大于等于目标的最小元素 | null |
higher(e) / higherKey(k) | 找到严格大于目标的最小元素 | null |
此外,它们还支持高效的范围视图(Range Views),如 subMap()、headMap() 和 tailMap(),能在 O ( log n ) O(\log n) O(logn) 时间内获取特定区间的数据视图。
找邻居的方法可能返回null,一定要注意判断,这里有一个小技巧:在判读离目标数最近的数时,我们可以先判断是否为空和是否有元素本身,这样如果floor返回为空,那最近的就是ceiling.

二、 TreeSet:唯一的有序集合
TreeSet 实现了 NavigableSet 接口,主要用于存储不重复的有序元素。
- 内部机制:TreeSet 实际上是基于 TreeMap 实现的。它在内部维护了一个
TreeMap实例,将元素存储为该 Map 的 Key,而 Value 则使用一个统一的虚拟对象。 - 元素唯一性:它不允许存储重复元素。TreeSet 判断“重复”的标准不是
equals(),而是compareTo()或compare()返回 0。 - Null 处理:Java 7 之后,TreeSet 不再支持存储 null 元素,否则会抛出
NullPointerException。

三、 TreeMap:功能强大的有序映射
TreeMap 存储键值对(Key-Value pairs),并根据 Key 进行排序。
- Key 的要求:作为 Key 的类必须实现
Comparable接口,或者在构造 Map 时提供Comparator,否则在插入时会抛出ClassCastException。 - Null 策略:TreeMap 不允许 null 作为 Key(因为无法进行比较),但允许存储多个 null 作为 Value。
- 线程安全:TreeMap 不是线程安全的。若需在多线程环境使用,建议使用
Collections.synchronizedNavigableMap()进行包装。

四、 核心底层:红黑树 (Red-Black Tree)
TreeSet 和 TreeMap 的核心竞争力在于它们的底层实现——红黑树。这是一种自平衡的二叉搜索树,具有以下关键特性:
- 时间复杂度:插入、删除、搜索等基本操作的时间复杂度均保证在 O ( log n ) O(\log n) O(logn)。
- 有序性:元素始终根据其键(Key)的自然顺序(实现
Comparable接口)或在创建时提供的Comparator进行排序。 - 空间复杂度:由于每个节点(Entry)都需要存储父节点、左右子节点引用及颜色信息,其内存开销通常高于基于数组的结构(如
HashMap),在 64 位 JVM 中每个 Entry 约占 40 字节。

五、 TreeSet/TreeMap vs HashMap/HashSet
| 特性 | TreeMap / TreeSet | HashMap / HashSet |
|---|---|---|
| 排序 | 始终保持有序 | 无序 |
| 性能 | O ( log n ) O(\log n) O(logn) | 平均 O ( 1 ) O(1) O(1) |
| 底层实现 | 红黑树 | 哈希表(数组+链表/红黑树) |
六、 开发建议与避坑指南
- 哨兵技巧(Sentinel Values):在处理寻找“前驱”或“后继”的逻辑时,为避免频繁的
null判断,可以预先向集合中插入“正负无穷大”作为哨兵值。 - 避免 NPE:当使用
floor()或ceiling()返回包装类(如Integer)并直接拆箱给基本类型int时,如果返回值为null,会触发空指针异常。 - 选择建议:如果你的应用场景只需要快速存取且不关心顺序,HashMap 是性能更好的选择。但如果你需要频繁进行范围搜索、寻找最接近的键或需要数据始终有序,请务必选择 TreeMap/TreeSet。

到此这篇关于深入浅出 Java 有序集合:TreeSet 与 TreeMap实例详解的文章就介绍到这了,更多相关java treeset与treemap 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
