Comparable y Comparator en Java son muy útiles para ordenar la colección de objetos. Java proporciona algunos métodos integrados para ordenar arrays de tipos primitivos o arrays de clases Wrapper o listas. Aquí primero aprenderemos cómo podemos ordenar un array/lista de tipos primitivos y clases wrapper, y luego utilizaremos las interfaces \texttt{java.lang.Comparable} y \texttt{java.util.Comparator} para ordenar arrays/listas de clases personalizadas. Veamos cómo podemos ordenar tipos primitivos o arrays de objetos y listas con un programa simple.
package com.journaldev.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaObjectSorting {
/**
* This class shows how to sort primitive arrays,
* Wrapper classes Object Arrays
* @param args
*/
public static void main(String[] args) {
//ordenar un array de tipos primitivos como un array de int
int[] intArr = {5,9,1,10};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr));
//ordenar un array de String
String[] strArr = {"A", "C", "B", "Z", "E"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr));
//ordenar lista de objetos de clases Wrapper
List strList = new ArrayList();
strList.add("A");
strList.add("C");
strList.add("B");
strList.add("Z");
strList.add("E");
Collections.sort(strList);
for(String str: strList) System.out.print(" "+str);
}
}
La salida del programa anterior es:
[1, 5, 9, 10]
[A, B, C, E, Z]
A B C E Z
Ahora intentemos ordenar un array de objetos.
package com.journaldev.sort;
public class Employee {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
//esto está sobrescrito para imprimir información amigable al usuario sobre el Empleado
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
Aquí está el código que usé para ordenar el array de objetos Empleado.
//ordenar un array de objetos
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//ordenar array de empleados usando la implementación de la interfaz Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
Cuando intenté ejecutar esto, arroja la siguiente excepción en tiempo de ejecución.
Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)
Comparable y Comparator
Java proporciona la interfaz Comparable, la cual debe ser implementada por cualquier clase personalizada si queremos utilizar los métodos de ordenación de Arrays o Collections. La interfaz Comparable tiene el método compareTo(T obj), que es utilizado por los métodos de ordenación; puedes verificar cualquier clase Wrapper, String o Date para confirmar esto. Debemos sobrescribir este método de tal manera que devuelva un entero negativo, cero o un entero positivo si “este” objeto es menor que, igual a, o mayor que el objeto pasado como argumento. Después de implementar Comparable interface en la clase Employee, aquí está la clase Employee resultante.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//vamos a ordenar los empleados según un id en orden ascendente
//devuelve un entero negativo, cero o un entero positivo según el id de este empleado
//sea menor que, igual a, o mayor que el objeto especificado.
return (this.id - emp.id);
}
@Override
//esto es necesario para imprimir la información amigable del Usuario sobre el Empleado
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
Ahora, cuando ejecutamos el fragmento anterior para la ordenación de Arrays de Empleados e imprimimos, aquí está la salida.
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Como se puede ver, la matriz de empleados está ordenada por ID en orden ascendente. Sin embargo, en la mayoría de los escenarios de la vida real, deseamos ordenar según diferentes parámetros. Por ejemplo, como CEO, me gustaría ordenar a los empleados según el salario, mientras que un profesional de recursos humanos desearía ordenarlos por edad. Esta es la situación en la que necesitamos utilizar la interfaz Java Comparator porque la implementación del método Comparable.compareTo(Object o) puede proporcionar una clasificación predeterminada y no podemos cambiarla dinámicamente. En cambio, con Comparator, podemos definir múltiples métodos con diferentes formas de ordenación y luego elegir el método de ordenación según nuestros requisitos.
Java Comparator
La interfaz Comparator requiere la implementación del método compare(Object o1, Object o2) que toma dos argumentos de tipo Object, y debería implementarse de manera que devuelva un entero negativo si el primer argumento es menor que el segundo, cero si son iguales y un entero positivo si el primer argumento es mayor que el segundo. Las interfaces Comparable y Comparator utilizan genéricos para la comprobación de tipos en tiempo de compilación. Obtenga más información sobre Java Generics. Así es como podemos crear diferentes implementaciones de Comparator en la clase Employee.
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
Todas las implementaciones anteriores de la interfaz Comparator son clases anónimas. Podemos usar estos comparadores para pasar un argumento a la función de ordenamiento de las clases Arrays y Collections.
// ordenar la matriz de empleados usando Comparator por salario
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
// ordenar la matriz de empleados usando Comparator por edad
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
// ordenar la matriz de empleados usando Comparator por nombre
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
Aquí está la salida del fragmento de código anterior:
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Así que ahora sabemos que si queremos ordenar una matriz u lista de objetos Java, necesitamos implementar la interfaz Comparable de Java para proporcionar una ordenación predeterminada y debemos implementar la interfaz Comparator de Java para proporcionar diferentes formas de ordenación. También podemos crear una clase separada que implemente la interfaz Comparator
y luego usarla. Aquí están las clases finales que explican Comparable y Comparator en Java.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
// ordenemos el empleado según un id en orden ascendente
// devuelve un entero negativo, cero o un entero positivo según si este id de empleado
// es menor que, igual a, o mayor que el objeto especificado.
return (this.id - emp.id);
}
@Override
// esto es necesario para imprimir información amigable para el usuario sobre el Empleado
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator SalaryComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator AgeComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator NameComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
}
Aquí está la implementación de la clase separada de la interfaz Comparator que comparará dos objetos de Empleado primero por su id y, si son iguales, por el nombre.
package com.journaldev.sort;
import java.util.Comparator;
public class EmployeeComparatorByIdAndName implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
int flag = o1.getId() - o2.getId();
if(flag==0) flag = o1.getName().compareTo(o2.getName());
return flag;
}
}
Esta es la clase de prueba donde estamos utilizando diferentes formas de ordenar objetos en Java utilizando Comparable y Comparator.
package com.journaldev.sort;
import java.util.Arrays;
public class JavaObjectSorting {
/**
* This class shows how to sort custom objects array/list
* implementing Comparable and Comparator interfaces
* @param args
*/
public static void main(String[] args) {
//ordenar matriz de objetos personalizados
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//ordenar matriz de empleados usando la implementación de la interfaz Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
//ordenar matriz de empleados usando Comparator por salario
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//ordenar matriz de empleados usando Comparator por edad
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//ordenar matriz de empleados usando Comparator por nombre
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
//Lista de empleados ordenada por ID y luego por nombre usando la clase Comparator
empArr[0] = new Employee(1, "Mikey", 25, 10000);
Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
}
}
Aquí está la salida del programa anterior:
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]
Las interfaces java.lang.Comparable y java.util.Comparator son interfaces poderosas que se pueden utilizar para ordenar objetos en Java.
Comparable vs Comparator
- La interfaz Comparable se puede utilizar para proporcionar una forma única de ordenar, mientras que la interfaz Comparator se utiliza para proporcionar diferentes formas de ordenar.
- Para usar Comparable, la clase debe implementarlo, mientras que para usar Comparator no necesitamos realizar ningún cambio en la clase.
- La interfaz Comparable está en el paquete
java.lang
, mientras que la interfaz Comparator está presente en el paquetejava.util
. - No es necesario realizar cambios de código en el lado del cliente para usar Comparable; los métodos
Arrays.sort()
oCollection.sort()
utilizan automáticamente el métodocompareTo()
de la clase. Para Comparator, el cliente debe proporcionar la clase Comparator para usar en el método compare().
¿Sabes que el método Collections.sort() que toma un argumento Comparator sigue el Patrón de Estrategia?
Puedes revisar el código completo y más ejemplos de Java básico en nuestro Repositorio de GitHub.
Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example