(de façons de trier les listes d'objets en Java en fonction de plusieurs champs )
Code de travail dans cet essentiel
Utilisation des lambda de Java 8 (ajouté le 10 avril 2019)
Java 8 résout bien cela par lambda (bien que Guava et Apache Commons puissent encore offrir plus de flexibilité):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Merci à la réponse de @ gaoagong ci-dessous .
Désordonné et alambiqué: Tri à la main
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);
}
});
Cela nécessite beaucoup de frappe, de maintenance et est sujet aux erreurs.
La manière réfléchie: Tri avec BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Évidemment, cela est plus concis, mais encore plus sujet aux erreurs car vous perdez votre référence directe aux champs en utilisant des chaînes à la place (pas de sécurité de type, auto-refactorisation). Maintenant, si un champ est renommé, le compilateur ne signalera même pas de problème. De plus, cette solution utilisant la réflexion, le tri est beaucoup plus lent.
Comment y arriver: Trier avec la chaîne de comparaison de 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();
*/
}
});
C'est beaucoup mieux, mais nécessite un code de plaque de chaudière pour le cas d'utilisation le plus courant: les valeurs nulles doivent être moins valorisées par défaut. Pour les champs nuls, vous devez fournir une directive supplémentaire à Guava que faire dans ce cas. C'est un mécanisme flexible si vous voulez faire quelque chose de spécifique, mais vous voulez souvent le cas par défaut (c'est-à-dire 1, a, b, z, null).
Tri avec 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();
}
});
Comme ComparisonChain de Guava, cette classe de bibliothèque trie facilement sur plusieurs champs, mais définit également le comportement par défaut pour les valeurs nulles (c'est-à-dire 1, a, b, z, null). Cependant, vous ne pouvez pas spécifier autre chose non plus, sauf si vous fournissez votre propre comparateur.
Donc
En fin de compte, cela se résume à la saveur et au besoin de flexibilité (ComparaisonChain de Guava) par rapport au code concis (CompareToBuilder d'Apache).
Méthode bonus
J'ai trouvé une belle solution qui combine plusieurs comparateurs par ordre de priorité sur CodeReview dans un 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 a déjà une utilité pour cela:
ComparatorUtils.chainedComparator (comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));