(最初来自基于多个字段对Java对象列表进行排序的方式)
要点中的原始工作代码
使用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));
感谢@gaoagong在下面的回答。
请注意,这里的一个优点是可以对吸气剂进行延迟评估(例如getSchool()
,仅在相关时评估)。
凌乱和混乱:手工排序
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);
}
});
这需要大量的输入,维护并且容易出错。唯一的好处是,仅在相关时才调用getter。
反射方式:使用BeanComparator排序
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
显然,这更加简洁,但是当您通过使用字符串(没有类型安全性,自动重构)而失去对字段的直接引用时,更容易出错。现在,如果重命名了字段,则编译器甚至不会报告问题。而且,由于此解决方案使用反射,因此排序速度要慢得多。
到达那里:使用Google Guava的ComparisonChain进行排序
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();
}
});
这样做要好得多,但是对于最常见的用例,需要一些样板代码:默认情况下,空值的值应较小。对于空字段,您必须提供一个额外的指令给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)的默认行为。但是,除非您提供自己的比较器,否则您也不能指定其他任何内容。
再次,如下面的评论中所述,对于每个比较,这些吸气剂都将立即进行评估。
从而
最终归结为风味和灵活性的需求(Guava的CompareChain)与简洁的代码(Apache的CompareToBuilder)。
奖金法
我找到了一个不错的解决方案,该解决方案在CodeReview中的优先级顺序中组合了多个比较器MultiComparator
:
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));