TreeSet基于红黑树实现,插入即有序,时间复杂度O(log n),依赖Comparable或Comparator排序,去重依据比较结果为0而非equals(),不支持随机访问。

TreeSet 默认按自然顺序排序,本质是红黑树维护的有序结构
TreeSet 不是简单调用 Arrays.sort() 那类临时排序,它从插入第一元素起就维持整体有序。底层用的是 TreeMap 实例——所有元素作为 key 存入,value 固定为 PRESENT(一个空对象)。这意味着每次 add() 都触发红黑树的插入+自平衡操作,时间复杂度稳定在 O(log n),而非数组排序的 O(n log n)。
关键点在于:TreeSet 本身不重写比较逻辑,它完全依赖元素是否实现 Comparable 接口,或构造时传入 Comparator。没提供且元素不可比,运行时抛 ClassCastException,不是编译报错。
自定义排序必须显式传 Comparator,不能靠重写 toString 或 equals
常见误区是以为重写 toString() 或 equals() 能影响 TreeSet 排序——完全无效。排序唯一入口是 Comparator 或 Comparable.compareTo()。
- 若元素类已实现
Comparable(如String、Integer),直接 new TreeSet() 即可 - 若想按不同规则排序(比如
Person按年龄而非姓名),必须传Comparator实例,例如:TreeSet
set = new TreeSet<>((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())); - Lambda 中避免直接用
p1.getAge() - p2.getAge(),整数溢出会导致错误排序(如 -2000000000 和 2000000000 相减越界)
add() 失败不报错,但 size 不变——这是去重逻辑在生效
TreeSet 是 Set,天然去重。判断“重复”的依据不是 equals(),而是比较结果为 0:compare(a, b) == 0 或 a.compareTo(b) == 0。这点和 HashSet 完全不同。
立即学习“Java免费学习笔记(深入)”;
因此可能出现:两个对象 equals() 返回 false,但因 compareTo() 返回 0,被 TreeSet 当作同一元素拒绝插入。
- 调试时发现
add()返回false且size()没变,优先检查比较逻辑是否把不该等价的对象判为等价 - 如果业务上需要保留“内容不同但排序值相同”的对象(比如多个同分数学生),TreeSet 不适用,该换
PriorityQueue或手动维护List + Collections.sort() - 注意
Comparator.nullsFirst()/nullsLast()的使用场景——当字段可能为 null 时,不包装会直接抛NullPointerException
遍历顺序永远有序,但迭代器不支持快速随机访问
TreeSet 的 iterator() 返回的是红黑树中序遍历结果,所以 for-each 或 stream() 都天然升序。但别试图用索引取第 N 个元素——它没有 get(int index) 方法,也没有基于数组的随机访问能力。
若需按排名查元素(如“分数第 3 高的学生”),要么转成 ArrayList 再取索引(代价 O(n)),要么用 TreeSet 的 higher()、lower()、ceiling() 等导航方法逼近,但这些只适合找邻近值,不适合精确排名。
真正需要频繁按秩访问的场景,TreeSet 就不是最优解——这时候该考虑 Apache Commons Collections 的 TreeList,或自己封装带 rank 维护的结构。









