TreeSet自动排序的根本原因是底层基于TreeMap和红黑树,插入时即按compareTo()或Comparator定位节点并保持平衡,时间复杂度O(log n)。

TreeSet 能自动排序,根本原因不是它“想排序”,而是它底层用的是 TreeMap,而 TreeMap 的 key 是靠红黑树维护顺序的——插入时就排好,不是等你遍历时才 sort。
为什么 add() 之后立马有序?——红黑树在写入时就定位节点
每次调用 set.add(e),实际执行的是 map.put(e, PRESENT)。红黑树根据 compareTo() 或 compare() 的返回值决定把新节点挂到左子树(更小)还是右子树(更大),同时自动做旋转和 recolor 来保持平衡。
- 时间复杂度稳定在
O(log n),不依赖后续遍历或额外排序 - 所以
first()、higher(3)、subSet(2, 5)这些操作也直接走树结构,不用扫描全集 - 如果你用
Arrays.sort()对 ArrayList 排序,那是O(n log n)且只在调用时生效;TreeSet 是“持续有序”
自然排序失效的典型错误:ClassCastException 或 NullPointerException
报 ClassCastException?说明你往空参构造的 TreeSet 里塞了没实现 Comparable 的自定义类;报 NullPointerException?大概率是 add 了 null,而默认比较器不允许。
- 内置类型如
Integer、String没问题,因为它们已实现Comparable - 自定义类必须实现
Comparable并重写compareTo(),且逻辑要覆盖所有字段(比如年龄相同时比姓名) -
null在自然排序下一定会触发 NPE;若真要支持,只能用带Comparator的构造器,并显式判空(但不推荐)
自定义排序别只写 lambda:注意 compare() 返回值语义
用 new TreeSet((a, b) -> ...) 很方便,但返回值含义常被搞反:正数表示 a > b(a 应排在 b 后面),负数才是 a 。
立即学习“Java免费学习笔记(深入)”;
TreeSetset = new TreeSet<>((s1, s2) -> Integer.compare(s2.length(), s1.length())); set.add("hi"); set.add("hello"); set.add("a"); // 结果是 ["hello", "hi", "a"] —— 按长度降序
- 降序别硬写
s1.compareTo(s2) * -1,用Comparator.reverseOrder()更安全 - 如果比较字段可能为 null(如
person.getName()),必须先判空,否则运行时 NPE - 同一个类需要多种排序逻辑时,优先用外部
Comparator,而不是改compareTo()——避免耦合
最容易被忽略的一点:TreeSet 里的对象,一旦存进去,就**不能修改影响排序的字段**。比如 Person 按 age 排序后你还 p.setAge(99),这个元素在红黑树里的位置就“错乱”了——contains() 可能找不到,remove() 可能失败,甚至遍历顺序异常。这不是 bug,是设计使然。









