Cómo domar el caos: ordenamos un ArrayList de objetos por cualquier campo en Java
\n\nImagine: está escribiendo una aplicación para un catálogo de libros. Tiene una clase Book con los campos title, author, price y year. Almacena decenas de libros en un ArrayList. Y entonces el usuario pide: «¡Muestra primero los más nuevos!», y un minuto después — «No, ordénalos por precio».
Si alguna vez se ha enfrentado a esta tarea, sabrá que simplemente llamar a Collections.sort(list) no funcionará. Java no puede leer la mente. Necesita que le expliquemos: por qué campo exactamente ordenar. De esto trata exactamente la pregunta más popular en Stack Overflow sobre la ordenación de objetos personalizados.
En este artículo, analizaremos todas las formas de ordenar un ArrayList por una propiedad de un objeto: desde el clásico Comparable hasta las elegantes expresiones lambda y Stream API. ¡Allá vamos!
Preparación: la clase que vamos a ordenar
\n\nPara los ejemplos, crearemos una clase simple Person:
public class Person {\n private String name;\n private int age;\n private double salary;\n\n public Person(String name, int age, double salary) {\n this.name = name;\n this.age = age;\n this.salary = salary;\n }\n\n // Getters (omitimos setters por brevedad)\n public String getName() { return name; }\n public int getAge() { return age; }\n public double getSalary() { return salary; }\n\n @Override\n public String toString() {\n return name + " (" + age + " años, $" + salary + ")";\n }\n}\n\n\nY llenamos nuestra lista:
\n\nArrayList<Person> people = new ArrayList<>();\npeople.add(new Person("Ana", 28, 55000));\npeople.add(new Person("Boris", 35, 72000));\npeople.add(new Person("Víctor", 22, 48000));\npeople.add(new Person("Galina", 30, 62000));\n\n\nAhora todo está listo para los experimentos. ¿Cómo ordenar esta lista por edad? ¿Por nombre? ¿Por salario? Java tiene al menos tres formas. Empecemos por la fundamental.
\n\nForma 1: Comparable — integramos la lógica de ordenación directamente en la clase
\n\nLa interfaz Comparable es el «orden natural» del objeto. Si desea que sus objetos siempre se ordenen por un mismo campo (por ejemplo, por edad), implemente esta interfaz.
Modifiquemos nuestra clase Person:
public class Person implements Comparable<Person> {\n // ... campos, constructor, getters ...\n\n @Override\n public int compareTo(Person other) {\n return Integer.compare(this.age, other.age);\n }\n}\n\n\nAhora la ordenación es una sola línea:
\n\nCollections.sort(people);\nSystem.out.println(people);\n// Salida: [Víctor (22 años, $48000), Ana (28 años, $55000), Galina (30 años, $62000), Boris (35 años, $72000)]\n\n\nVentajas: simplicidad, no hay que pensar en comparadores externos.
\nDesventajas: «incrusta» en la clase solo una forma de ordenación. Si mañana necesita ordenar por nombre, tendrá que modificar la clase.
Por cierto, para el orden inverso (de mayor a menor) puede usar Collections.reverseOrder():
Collections.sort(people, Collections.reverseOrder());\n\n\nForma 2: Comparator — flexibilidad sin modificar la clase
\n\nCuando necesita ordenar de diferentes maneras en diferentes partes del programa, Comparator viene al rescate. Es una clase separada o una clase anónima que contiene la lógica de comparación.
Clase anónima — el método del abuelo
\n\nCollections.sort(people, new Comparator<Person>() {\n @Override\n public int compare(Person p1, Person p2) {\n return p1.getName().compareTo(p2.getName());\n }\n});\n\n\nFunciona, pero parece voluminoso. En