(pierwotnie ze sposobów sortowania list obiektów w Javie na podstawie wielu pól )
Oryginalny działający kod w tym istocie
Korzystanie z lambda Java 8 (dodano 10 kwietnia 2019 r.)
Java 8 ładnie rozwiązuje to za pomocą lambda (chociaż Guava i Apache Commons mogą nadal oferować większą elastyczność):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Dzięki poniższej odpowiedzi @ gaoagong .
Zwróć uwagę, że jedną z zalet jest to, że metody pobierające są oceniane leniwie (np. Są oceniane getSchool()
tylko wtedy, gdy są istotne).
Niechlujny i zawiły: sortowanie ręczne
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);
}
});
Wymaga to dużo pisania, konserwacji i jest podatne na błędy. Jedyną zaletą jest to, że metody pobierające są wywoływane tylko wtedy, gdy są istotne.
Odblaskowy sposób: sortowanie za pomocą BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Oczywiście jest to bardziej zwięzłe, ale jeszcze bardziej podatne na błędy, ponieważ tracisz bezpośrednie odniesienie do pól, używając zamiast tego ciągów znaków (brak bezpieczeństwa typów, autorefaktoryzacja). Teraz, jeśli nazwa pola zostanie zmieniona, kompilator nawet nie zgłosi problemu. Co więcej, ponieważ to rozwiązanie wykorzystuje odbicie, sortowanie przebiega znacznie wolniej.
Jak się tam dostać: sortowanie za pomocą porównywarki 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();
}
});
Jest to o wiele lepsze, ale wymaga trochę kodu kotłowego dla najczęstszego przypadku użycia: wartości null powinny być domyślnie wyceniane mniej. W przypadku pól o wartości null musisz przekazać Guava dodatkową dyrektywę, co robić w takim przypadku. Jest to elastyczny mechanizm, jeśli chcesz zrobić coś konkretnego, ale często potrzebujesz domyślnej wielkości liter (np. 1, a, b, z, null).
Jak zauważono w komentarzach poniżej, wszystkie te metody pobierające są oceniane natychmiast dla każdego porównania.
Sortowanie za pomocą 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();
}
});
Podobnie jak CompareChain firmy Guava, ta klasa biblioteki łatwo sortuje na wielu polach, ale także definiuje domyślne zachowanie dla wartości null (np. 1, a, b, z, null). Jednak nie możesz też określić niczego innego, chyba że zapewnisz własny komparator.
Ponownie, jak zauważono w komentarzach poniżej, wszystkie te metody pobierające są oceniane natychmiast dla każdego porównania.
A zatem
Ostatecznie sprowadza się to do smaku i potrzeby elastyczności (Guava's CompareChain) w porównaniu do zwięzłego kodu (PorównajToBuilder Apache).
Metoda bonusowa
Znalazłem fajne rozwiązanie, które łączy wiele komparatorów w kolejności priorytetów w CodeReview w 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));
}
}
Oczywiście Apache Commons Collections ma już do tego zastosowanie:
ComparatorUtils.chainedComparator (ComparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));