假设有下面这样的用户类,此时你需要对它的列表进行排序,先按照id降序,再按照age降序。
/**
* 用户类
*/
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 + "}";
}
}
那么你很可能写出下面这样的示例代码:
/**
* 示例代码
*/
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() 开始分析
@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;
// 🎯 <1> 使用ReverseComparator类型装饰
return new ReverseComparator2<>(cmp);
}
}
可以看到位置<1>,reverseOrder()方法会将入参使用ReverseComparator类型装饰。此类是Collections的静态内部类,其代码如下:
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;
}
// 🎯 <2>参数交换了顺序,也就是之前的排序规则都反转了
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()方法将会反转前面的排序规则。
// 先按照id降序,再按照age降序
Comparator.comparingLong(User::getId)
.thenComparingInt(User::getAge).reversed()
// 先按照id升序,再按照age降序
Comparator.comparingLong(User::getId).reversed()
.thenComparingInt(User::getAge).reversed()
吐槽:虽然Comparator类使用@FunctionalInterface声明,但里面很多default修饰的默认方法