(originalmente de Maneras de ordenar listas de objetos en Java en base a múltiples campos )
Código de trabajo original en esta esencia
Usando Java 8 lambda (agregado el 10 de abril de 2019)
Java 8 resuelve esto muy bien mediante lambda (aunque Guava y Apache Commons aún pueden ofrecer más flexibilidad):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Gracias a la respuesta de @gaoagong a continuación .
Tenga en cuenta que una ventaja aquí es que los captadores se evalúan de manera perezosa (por ejemplo, getSchool()
solo se evalúa si es relevante).
Desordenado y complicado: clasificación a mano
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);
}
});
Esto requiere mucho mecanografiado, mantenimiento y es propenso a errores. La única ventaja es que los captadores solo se invocan cuando son relevantes.
La forma reflexiva: clasificación con BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Obviamente, esto es más conciso, pero aún más propenso a errores, ya que pierde su referencia directa a los campos al usar Strings en su lugar (sin seguridad de tipos, refactorizaciones automáticas). Ahora, si se cambia el nombre de un campo, el compilador ni siquiera informará un problema. Además, debido a que esta solución utiliza la reflexión, la clasificación es mucho más lenta.
Cómo llegar: clasificación con ComparisonChain 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();
}
});
Esto es mucho mejor, pero requiere algún código de placa de caldera para el caso de uso más común: los valores nulos deben valorarse menos por defecto. Para los campos nulos, debe proporcionar una directiva adicional a Guava sobre qué hacer en ese caso. Este es un mecanismo flexible si desea hacer algo específico, pero a menudo desea el caso predeterminado (es decir, 1, a, b, z, nulo).
Y como se indica en los comentarios a continuación, todos estos captadores se evalúan inmediatamente para cada comparación.
Ordenar con 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();
}
});
Al igual que ComparisonChain de Guava, esta clase de biblioteca se clasifica fácilmente en varios campos, pero también define el comportamiento predeterminado para valores nulos (es decir, 1, a, b, z, nulo). Sin embargo, tampoco puede especificar nada más, a menos que proporcione su propio Comparador.
Nuevamente, como se indica en los comentarios a continuación, todos estos captadores se evalúan inmediatamente para cada comparación.
Así
En última instancia, todo se reduce al sabor y la necesidad de flexibilidad (ComparisonChain de Guava) frente al código conciso (CompareToBuilder de Apache).
Método de bonificación
Encontré una buena solución que combina múltiples comparadores en orden de prioridad en CodeReview en 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));
}
}
Por supuesto, Apache Commons Collections ya tiene una utilidad para esto:
ComparatorUtils.chainedComparator (comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));