TreeSet默认按自然顺序排序,要求元素实现Comparable接口,否则抛ClassCastException;若传入Comparator则以其为准,忽略compareTo,且compare返回0时视为重复元素不插入。

TreeSet 默认按自然顺序排序,但必须元素实现 Comparable
TreeSet 底层基于红黑树,插入时自动排序。若不传入自定义比较器,它会调用元素自身的 compareTo() 方法 —— 这要求元素类型必须实现 Comparable 接口。否则运行时报 ClassCastException。
- 常见错误:往
TreeSet里 add 字符串缓冲对象 →StringBuffer没实现Comparable,直接抛异常 - 正确做法:自定义类如
Person,需重写compareTo(),且逻辑必须满足自反性、对称性、传递性 - 注意:
compareTo()返回负数表示“小于”,0 表示“等于”,正数表示“大于”——别反着写
用 Comparator 构造 TreeSet,绕过类自身限制
当无法修改原始类(比如第三方库的类),或需要多种排序逻辑(如按姓名升序、按年龄降序),应传入 Comparator 实现。
- 构造方式:
TreeSet
set = new TreeSet<>((p1, p2) -> p1.getAge() - p2.getAge()); - Lambda 最简写法只适用于简单字段比较;涉及 null 安全或复杂逻辑,建议单独写类或静态方法
- 若比较器返回 0,TreeSet 认为两个元素相等,**第二个不会被加入**——这和 List 不同,是去重逻辑的一部分
- 比较器中抛出异常(如空指针)会导致 add 失败且集合状态可能已部分变更
Comparator 和 Comparable 冲突时谁生效?
以构造时传入的 Comparator 为准。TreeSet 创建后就绑定该比较器,完全忽略元素自身的 compareTo()。
- 典型误用:类实现了
Comparable(按 name 排序),但构造 TreeSet 时传了按 age 的Comparator,结果还是按 age 排 —— 这不是 bug,是设计如此 - 调试技巧:在比较器 lambda 里加
System.out.println,确认是否被调用,避免误以为“没生效”而去改compareTo - 注意:同一个 TreeSet 实例不能动态切换比较逻辑;要换规则,得新建实例
排序字段值相同时,TreeSet 会丢数据,这不是 bug 是机制
TreeSet 的“排序 + 去重”是一体行为:只要 compare(a,b) == 0,就认为 a 和 b 是重复元素,后者被拒绝插入。
立即学习“Java免费学习笔记(深入)”;
- 例如:两个
Person("张三", 25)对象,按 age 比较器返回 0 → 第二个加不进去 - 解决办法不是强行让比较器返回非零值(破坏排序一致性),而是确保业务上“相同”意味着真重复;否则改用
TreeMap或先用> List排序再去重 - 特别容易踩坑:用浮点字段(如
double score)做比较器主键,因精度问题导致本该相等的值比较结果非零,反而多存了“重复”项
实际用的时候,先想清楚:这个集合到底要不要严格去重?如果只是想“有序遍历”,但允许重复值,TreeSet 就不是合适选择。










