Comparable是类自身实现的自然排序协议,强制定义compareTo()方法返回负数/0/正数表示小于/等于/大于,需与equals()逻辑一致;Comparator是外部注入的函数式接口,支持灵活、可复用的临时排序规则。
一个类实现 Comparable 接口,等于向外界声明:“我天生就知道怎么跟同类比大小”。它强制定义了 compareTo() 方法,返回负数、0 或正数,分别表示“小于”“等于”“大于”。
常见错误是让 compareTo() 逻辑和 equals() 不一致——比如两个对象 equals() 返回 true,但 compareTo() 却不返回 0。这会导致 TreeSet 或 TreeMap 把它们当成不同元素,引发去重失效或查找异常。
Integer 比数值、String 比字典序)compareTo() 中避免空指针:推荐用 Objects.compare(a, b, Comparator.naturalOrder()) 或 Integer.compare(x, y) 等工具方法Comparable 控制排序Comparator 是函数式接口,通过 compare(a, b) 定义临时、可变、可复用的比较规则。它不侵入类本身,适合多维度、条件化、一次性的排序需求。
典型误用是把 Comparator 实例当作线程安全对象反复复用,却在其中修改了共享状态(比如内部缓存未同步)。实际上,无状态的 lambda 或静态方法引用(如 String::compareToIgnoreCase)才是安全默认选择。
thenComparing() 而非手写嵌套三元表达式,可读性与空值处理更稳NullPointerException,需显式用 Comparator.nullsFirst() 或 nullsLast()
Stream.sorted()、Collections.sort()、TreeSet(Comparator) 中直接传入,无需额外包装TreeSet 和 TreeMap 的底层是红黑树,插入时必须立刻确定元素位置,因此要么元素类型实现 Comparable,要么构造时传入 Comparator;否则运行时报 ClassCastException。
而 ArrayList、LinkedList 这类列表不维护顺序,排序必须显式调用 Collections.sort(list, comparat 或 
list.sort(comparator);Stream 则完全依赖 sorted() 参数。
TreeSet 构造时没传 Comparator,且元素没实现 Comparable → 启动就崩,报错信息含 “cannot be cast to java.lang.Comparable”Collections.sort() 对 null 元素零容忍,哪怕 Comparator 支持 null,也要确保集合本身不含 null(除非你明确用了 nullsFirst())stream().sorted() 是惰性求值,错误延迟到终端操作(如 collect())才暴露,调试时容易漏掉源头排序过程会高频调用 compareTo() 或 compare(),有时单次排序触发上万次比较。如果在这些方法里查数据库、解析 JSON、格式化日期,性能会断崖式下跌,且极易因异常中断整个排序流程。
另一个隐蔽问题是浮点数比较。直接用 Double.compare(a, b) 是安全的;但若手动写 a - b > 0,不仅精度丢失,还可能产生 NaN 导致比较结果恒为 false。
LocalDateTime 转成 long 时间戳),在 compareTo() 中直接比数字// 示例:安全的多级 Comparator 链式写法 Listpeople = ...; people.sort(Comparator.comparing(Person::getAge) .thenComparing(Person::getName, String.CASE_INSENSITIVE_ORDER) .thenComparingLong(p -> p.getId()) .thenComparing(Comparator.nullsLast(Comparator.naturalOrder())));
Comparable 和 Comparator 看似只是“谁来定义排序”,实际决定了集合能否构建、排序是否可中断、并发下是否安全、边界数据是否静默失败——这些细节往往在压测或上线后才浮现。