后端踩坑Comparator reversed()踩坑
Jint
本文分享java代码下Comparator类reversed()排序的踩坑记录,而后探究相关源码,并做出总结。
假设有下面这样的用户类,此时你需要对它的列表进行排序,先按照id降序,再按照age降序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
class User { private long id; private int age;
public User(long id, int age) { this.id = id; this.age = age; }
public long getId() { return id; }
public int getAge() { return age; }
@Override public String toString() { return "User{id=" + id + ", age=" + age + "}"; } }
|
那么你很可能写出下面这样的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class Main { public static void main(String[] args) { List<User> users = Arrays.asList( new User(1L, 30), new User(2L, 25), new User(3L, 35), new User(3L, 30) ); Comparator<User> comparator = Comparator.comparingLong(User::getId).reversed() .thenComparingInt(User::getAge).reversed();
List<User> streamSortResult = users.stream() .sorted(comparator) .collect(Collectors.toList()); streamSortResult.forEach(System.out::println); } }
|
即使你问AI,它也会让你这样写,但其实这种写法是错误的。

实际运行上述代码,控制台打印的输出是下面这样的。很明显,实际输出结果是先按照id升序,再按照age降序。

源码探究
从 reversed() 开始分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @FunctionalInterface public interface Comparator<T> {
default Comparator<T> reversed() { return Collections.reverseOrder(this); }
public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) { if (cmp == null) return reverseOrder();
if (cmp instanceof ReverseComparator2) return ((ReverseComparator2<T>)cmp).cmp; return new ReverseComparator2<>(cmp); } }
|
可以看到位置<1>,reverseOrder()方法会将入参使用ReverseComparator类型装饰。此类是Collections的静态内部类,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Collections {
private static class ReverseComparator2<T> implements Comparator<T>, Serializable { private static final long serialVersionUID = 4374092139857L;
final Comparator<T> cmp;
ReverseComparator2(Comparator<T> cmp) { assert cmp != null; this.cmp = cmp; }
public int compare(T t1, T t2) { return cmp.compare(t2, t1); }
public boolean equals(Object o) { return (o == this) || (o instanceof ReverseComparator2 && cmp.equals(((ReverseComparator2)o).cmp)); }
public int hashCode() { return cmp.hashCode() ^ Integer.MIN_VALUE; }
@Override public Comparator<T> reversed() { return cmp; } } }
|
位置<2>中交换了参数顺序,那也就是反转了前面的排序规则
总结:
reversed()方法将会反转前面的排序规则。
1 2 3 4 5 6
| Comparator.comparingLong(User::getId) .thenComparingInt(User::getAge).reversed()
Comparator.comparingLong(User::getId).reversed() .thenComparingInt(User::getAge).reversed()
|
吐槽:虽然Comparator类使用@FunctionalInterface声明,但里面很多default修饰的默认方法