(来自Ways to sort lists of objects in Java based on multiple fields)
this gist中的工作代码
使用 Java 8 lambda(2019 年 4 月 10 日添加)
Java 8 通过 lambda 很好地解决了这个问题(尽管 Guava 和 Apache Commons 可能仍然提供更大的灵活性):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
感谢@gaaogong的answer below。
凌乱而复杂:手工排序
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
int sizeCmp = p1.size.compareTo(p2.size);
if (sizeCmp != 0) {
return sizeCmp;
}
int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);
if (nrOfToppingsCmp != 0) {
return nrOfToppingsCmp;
}
return p1.name.compareTo(p2.name);
}
});
这需要大量输入、维护并且容易出错。
反射方式:使用 BeanComparator 排序
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
显然这更简洁,但更容易出错,因为您使用字符串代替了对字段的直接引用(没有类型安全,自动重构)。现在,如果一个字段被重命名,编译器甚至不会报告问题。而且,因为这个方案使用了反射,所以排序要慢很多。
到达那里:使用 Google Guava 的比较链进行排序
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();
// or in case the fields can be null:
/*
return ComparisonChain.start()
.compare(p1.size, p2.size, Ordering.natural().nullsLast())
.compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast())
.compare(p1.name, p2.name, Ordering.natural().nullsLast())
.result();
*/
}
});
这要好得多,但对于最常见的用例需要一些样板代码:默认情况下,null-values 的值应该较小。对于空字段,您必须为 Guava 在这种情况下做什么提供额外的指令。如果您想做一些特定的事情,这是一种灵活的机制,但通常您需要默认情况(即 1、a、b、z、null)。
使用 Apache Commons CompareToBuilder 进行排序
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();
}
});
与 Guava 的 ComparisonChain 一样,这个库类可以轻松地对多个字段进行排序,而且还定义了 null 值的默认行为(即 1、a、b、z、null)。但是,您也不能指定任何其他内容,除非您提供自己的 Comparator。
因此
最终归结为风味和灵活性需求(Guava 的 ComparisonChain)与简洁的代码(Apache 的 CompareToBuilder)。
奖励方式
我找到了一个很好的解决方案,它在 MultiComparator 中按优先级顺序组合了多个比较器 on CodeReview:
class MultiComparator<T> implements Comparator<T> {
private final List<Comparator<T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
Collections.sort(list, new MultiComparator<T>(comparators));
}
}
当然,Apache Commons Collections 已经为此提供了一个实用工具:
ComparatorUtils.chainedComparator(comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));