TreeSet能排序是因为底层用TreeMap实现,而TreeMap基于红黑树维持键的自然顺序或Comparator指定顺序;元素须可比较,否则抛ClassCastException;操作时间复杂度O(log n),遍历天然有序。

TreeSet 能排序,不是因为它“想排序”,而是它底层用 TreeMap 实现,而 TreeMap 是基于红黑树(Red-Black Tree)的有序映射 —— 红黑树本身就能维持键的自然顺序或按指定 Comparator 排序。
TreeSet 底层不存元素,存的是 TreeMap 的 key
TreeSet 没有自己的存储结构,它的全部操作都委托给一个私有 TreeMap 实例:
- 每次调用
add(e),实际执行的是map.put(e, PRESENT),其中PRESENT是一个共享的哑值(static final Object) - 所以
TreeSet的“有序”完全继承自TreeMap对key的排序逻辑 - 这意味着:元素必须可比较(实现
Comparable),或构造时传入Comparator;否则运行时抛ClassCastException
红黑树保证 O(log n) 插入/查找 + 有序遍历
红黑树是自平衡二叉搜索树,它通过颜色标记和旋转维持近似平衡。这带来两个关键效果:
- 所有基本操作(
add、contains、remove)时间复杂度稳定在O(log n),不像ArrayList查找是O(n) - 中序遍历天然有序 —— 所以
TreeSet.iterator()返回的迭代器,遍历结果一定按排序顺序输出 - 注意:
TreeSet没有索引访问(不支持get(int index)),它不是列表,不能当有序数组用
自然顺序 vs 自定义 Comparator:行为差异很关键
排序依据不只看“能不能比”,更要看“比什么”。常见陷阱来自 equals() 和 compareTo() 不一致:
立即学习“Java免费学习笔记(深入)”;
- 若类实现
Comparable,但compareTo()逻辑与equals()冲突(比如只比 id,但equals()还看 name),TreeSet可能认为两个“逻辑相等”的对象不同,导致重复添加 - 构造时传
Comparator会覆盖类自身的compareTo();如果Comparator返回 0,TreeSet 就视为重复,直接拒绝添加 —— 即使equals()返回false - 空值处理:自然顺序下
null直接抛NullPointerException;自定义Comparator可显式处理null(如用Comparator.nullsFirst())
TreeSetset = new TreeSet<>(Comparator.nullsFirst(String::compareTo)); set.add("apple"); set.add(null); // OK set.add("banana");
红黑树的“有序”是严格的逻辑顺序,不是插入顺序,也不是哈希散列顺序。最容易忽略的一点是:TreeSet 的排序能力完全依赖比较逻辑的一致性 —— 一旦 compareTo() 在多次调用中返回结果不一致(比如依赖了可变字段),整个树结构就可能损坏,后续操作行为不可预测。










