(из Способы сортировки списков объектов в Java на основе нескольких полей )
Рабочий код в суть
Использование Java 8лямбда (добавлено 10 апреля 2019 г.)
Java 8 хорошо решает эту проблему с помощью лямбды (хотя Guava и Apache Commons могут по-прежнему предлагать большую гибкость):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Благодаря @ gaoagong's ответ ниже .
Грязно и запутанно: сортировка вручную
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 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();
// 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();
*/
}
});
Это намного лучше, но для кода требуется дополнительный кодНаиболее распространенный вариант использования: по умолчанию нулевые значения должны оцениваться меньше.Для нулевых полей вы должны предоставить 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();
}
});
Как и в ComparisonChain Guava, этот библиотечный класс легко сортируется по нескольким полям, но также определяет поведение по умолчанию для нулевых значений (т. Е. 1, a, b, z, null).Однако вы также не можете указать что-либо еще, если не предоставите свой собственный Comparator.
Таким образом
В конечном итоге все сводится к вкусу и необходимости в гибкости (ComparisonChain от Guava) в сравнении с лаконичным кодом(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));
}
}
Ofcourse Apache Commons Collections уже имеет утилиту для этого:
ComparatorUtils.chainedComparator (comptorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));