(изначально из Ways для сортировки списков объектов в Java на основе нескольких полей )
Исходный рабочий код в этом смысле
Использование лямбда-выражений Java 8 (добавлено 10 апреля 2019 г.)
Java 8 прекрасно решает эту проблему с помощью лямбды (хотя 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);
}
});
Это требует много ввода, обслуживания и подвержено ошибкам. Единственное преимущество в том, что геттеры вызываются только при необходимости.
Рефлексивный способ: сортировка с помощью BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Очевидно, это более лаконично, но еще больше подвержено ошибкам, поскольку вы теряете прямую ссылку на поля, используя вместо этого строки (без обеспечения безопасности типов, авторефакторинга). Теперь, если поле будет переименовано, компилятор даже не сообщит о проблеме. Более того, поскольку это решение использует отражение, сортировка выполняется намного медленнее.
Как добраться: сортировка с помощью ComparisonChain 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();
}
});
Это намного лучше, но для наиболее распространенного варианта использования требуется некоторый шаблонный код: по умолчанию нулевые значения должны иметь меньшее значение. Для нулевых полей вы должны предоставить 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) по сравнению с кратким кодом (CompareToBuilder от Apache).
Бонусный метод
Я нашел хорошее решение, которое объединяет несколько компараторов в порядке приоритета на 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));