descendingSet() 返回原 TreeSet 的实时反向视图,非新集合,修改视图即修改原集合;迭代顺序为自然倒序,不支持单独设 Comparator;含 null 时行为不稳定;性能 O(1),但线程不安全。

descendingSet 返回的是视图,不是新集合
调用 descendingSet() 得到的不是拷贝,而是原 TreeSet 的实时反向视图。对这个视图的修改(比如 add()、remove())会直接影响原集合,反之亦然。
常见错误是以为它能安全用于只读倒序遍历,结果在遍历时意外修改了原集合——比如在迭代 descendingSet() 时调用 remove(),可能触发 ConcurrentModificationException(取决于具体操作顺序和 JVM 实现)。
- 视图与原集合共享同一棵红黑树结构,只是遍历方向相反
- 不能用
new TreeSet(set.descendingSet())来“固化”倒序——这会重新排序,失去 O(1) 视图开销优势 - 若需不可变副本,应显式转成
List后反转:new ArrayList(set)+Collections.reverse()
descendingSet 的迭代顺序就是自然倒序
对于 Integer、String 等默认可比较类型,descendingSet() 迭代出来的元素就是从大到小(或字典序降序)。不需要额外指定 Comparator,除非你自定义了正向比较逻辑。
示例:
TreeSet<Integer> set = new TreeSet<>(Arrays.asList(1, 5, 3, 9));
for (int i : set.descendingSet()) {
System.out.print(i + " "); // 输出:9 5 3 1
}
- 如果构造
TreeSet时传入了自定义Comparator(比如升序按字符串长度),那descendingSet()就是按该规则的逆序(即降序按长度) - 不支持对
descendingSet()单独设置新Comparator;它的顺序完全由原集合决定
descendingSet 不支持 null 元素(即使原 TreeSet 支持)
哪怕你用 TreeSet 构造时传入了允许 null 的 Comparator(如 (a,b) -> a==null ? -1 : b==null ? 1 : Integer.compare(a,b)),descendingSet() 在某些 JDK 版本中仍可能抛 NullPointerException ——因为其内部实现未完全适配所有自定义空值逻辑。
- OpenJDK 8u292+ 对部分空值场景做了修复,但行为仍不稳定
- 最稳妥做法:避免在含
null的TreeSet上调用descendingSet() - 若必须处理空值且需要倒序,改用
Stream:set.stream().sorted(Collections.reverseOrder()).toList()
性能上几乎零开销,但要注意并发场景
descendingSet() 是 O(1) 操作,不复制数据、不重建树。但正因为它是强耦合视图,在多线程环境下极易出问题。
- 没有任何同步机制,多个线程同时读写原集合和它的
descendingSet()会导致数据不一致或异常 - 不要把它暴露给不可信代码(比如作为方法返回值)——调用方一个
clear()就清空了你的原集合 - 如需线程安全的倒序访问,优先考虑
ConcurrentSkipListSet(它也提供descendingSet(),且线程安全)


















