![]() |
Disponemos de una función que ordena listas de strings (no es necesario entender como funciona):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package paradigmas3c; import java.util.*; public class Cosas { public static void ordenar(List<String> lis) { // Ordenación burbuja int n = lis.size(); for(int i = 0; i < n-1; i++) { for(int j = n-1; j > i; j--) { String eant = lis.get(j-1); String eact = lis.get(j); if(eant.compareTo(eact) > 0) { // lis[j-1] > lis[j] -> Intercambiamos lis.set(j-1, eact); lis.set(j, eant); } } } } } |
Queremos generalizarla para que pueda ordenar listas de personas y alumnos con distintos criterios de ordenación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package paradigmas3c; import java.util.*; public class Paradigmas3c { public class Persona { public String DNI; public String Apellidos; public String Nombre; public Persona(String DNI, String Apellidos, String Nombre) { this.DNI = DNI; this.Apellidos = Apellidos; this.Nombre = Nombre; } @Override public String toString() { return String.format("%s | %s, %s", DNI, Apellidos, Nombre); } } public class Alumno extends Persona { public double Nota; public Alumno(String DNI, String Apellidos, String Nombre, double Nota) { super(DNI, Apellidos, Nombre); this.Nota = Nota; } @Override public String toString() { return String.format("%s | %s, %s: %.2f", DNI, Apellidos, Nombre, Nota); } } public void Procesar() { Persona p1 = new Persona("01234567A", "Perez Gomez", "Ana"); Persona p2 = new Persona("76543210B", "Alvarez Zamora", "Pedro"); Persona p3 = new Persona("66666666X", "Alvarez Alvarez", "Maria"); Persona p4 = new Persona("42424242Z", "Sainz Jimenez", "Javier"); Alumno a1 = new Alumno("01234567A", "Perez Gomez", "Ana", 8.7); Alumno a2 = new Alumno("76543210B", "Alvarez Zamora", "Pedro", 3.5); Alumno a3 = new Alumno("66666666X", "Alvarez Alvarez", "Maria", 7.5); Alumno a4 = new Alumno("43434343Z", "Sainz Ximenez", "Xavier", 8.2); List<Persona> lisper = Arrays.asList(p1, p2, p3, p4); List<Alumno> lisalu = Arrays.asList(a1, a2, a3, a4); Cosas.ordenar(lisper); System.out.println("LISTA DE PERSONAS"); lisper.forEach(System.out::println); Cosas.ordenar(lisalu); System.out.println("\nLISTA DE ALUMNOS"); lisalu.forEach(System.out::println); } public static void main(String[] args) { (new Paradigmas3c()).Procesar(); } } |
Se muestran en fondo rojo las zonas donde el compilador indica un error ya que una lista de personas o de alumnos no es compatible con una lista de strings
El parametrizar directamente no es suficiente, ya que es necesario que los elementos de la lista a ordenar (clase genérica T) dispongan de la operación compareTo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package paradigmas3c; import java.util.*; public class Cosas { public static <T> void ordenar(List<T> lis) { // Ordenación burbuja int n = lis.size(); for(int i = 0; i < n-1; i++) { for(int j = n-1; j > i; j--) { T eant = lis.get(j-1); T eact = lis.get(j); if(eant.compareTo(eact) > 0) { // lis[j-1] > lis[j] -> Intercambiamos lis.set(j-1, eact); lis.set(j, eant); } } } } } |
La interfaz Comparable de la librería estandar de Java obliga a que cualquier clase que la implemente incluya el método compareTo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package paradigmas3c; import java.util.*; public class Cosas { public static <T extends Comparable> void ordenar(List<T> lis) { // Ordenación burbuja int n = lis.size(); for(int i = 0; i < n-1; i++) { for(int j = n-1; j > i; j--) { T eant = lis.get(j-1); T eact = lis.get(j); if(eant.compareTo(eact) > 0) { // lis[j-1] > lis[j] -> Intercambiamos lis.set(j-1, eact); lis.set(j, eant); } } } } } |
Es necesario hacer que la clases Persona y Alumno implementen la interfaz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ... public class Persona implements Comparable { ... public Persona(String DNI, String Apellidos, String Nombre) { ... } @Override public String toString() { ... } @Override public int compareTo(Object o) { Persona otra = (Persona) o; return Apellidos.compareTo(otra.Apellidos); } } public class Alumno extends Persona { ... public Alumno(String DNI, String Apellidos, String Nombre, double Nota) { ... } @Override public String toString() { ... } @Override public int compareTo(Object o) { Alumno otro = (Alumno) o; if(Nota < otro.Nota) return -1; if(Nota > otro.Nota) return 1; return 0; } } ... |
El método usado en la sección anterior solo permite definir un criterio de ordenación por cada clase. Si por ejemplo quisieramos ordenar por apellidos y en otro punto del programa ordenar por dni, no podríamos hacerlo. Un enfoque más general es proporcionar un parámetro extra a la función ordenar que sea algo que indique la forma de comparar dos elementos.
En Java existe la interfaz genérica Comparator<T> que se usa para crear clases que incluyan el método compare(T a, T b) el cual define el código para comparar dos elementos de clase T
La función ordenar se modificaría de ésta forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package paradigmas3c; import java.util.*; public class Cosas { public static <T> void ordenar(List<T> lis, Comparator<T> comp) { // Ordenación burbuja int n = lis.size(); for(int i = 0; i < n-1; i++) { for(int j = n-1; j > i; j--) { T eant = lis.get(j-1); T eact = lis.get(j); if(comp.compare(eant, eact) > 0) { // lis[j-1] > lis[j] -> Intercambiamos lis.set(j-1, eact); lis.set(j, eant); } } } } } |
En el programa principal definimos las clases necesarias para indicar los criterios de ordenación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | package paradigmas3c; import java.util.*; public class Paradigmas3c { public class Persona { ... } public class Alumno extends Persona { ... } class CompDNI implements Comparator<Persona> { @Override public int compare(Persona o1, Persona o2) { return o1.DNI.compareTo(o2.DNI); } } class CompApellidos implements Comparator<Persona> { @Override public int compare(Persona o1, Persona o2) { return o1.Apellidos.compareTo(o2.Apellidos); } } class CompNota implements Comparator<Alumno> { @Override public int compare(Alumno o1, Alumno o2) { if(o1.Nota > o2.Nota) return -1; if(o1.Nota < o2.Nota) return 1; return 0; } } public void Procesar() { Persona p1 = new Persona("01234567A", "Perez Gomez", "Ana"); Persona p2 = new Persona("76543210B", "Alvarez Zamora", "Pedro"); Persona p3 = new Persona("66666666X", "Alvarez Alvarez", "Maria"); Persona p4 = new Persona("42424242Z", "Sainz Jimenez", "Javier"); Alumno a1 = new Alumno("01234567A", "Perez Gomez", "Ana", 8.7); Alumno a2 = new Alumno("76543210B", "Alvarez Zamora", "Pedro", 3.5); Alumno a3 = new Alumno("66666666X", "Alvarez Alvarez", "Maria", 7.5); Alumno a4 = new Alumno("43434343Z", "Sainz Ximenez", "Xavier", 8.2); List<Persona> lisper = Arrays.asList(p1, p2, p3, p4); List<Alumno> lisalu = Arrays.asList(a1, a2, a3, a4); Cosas.ordenar(lisper, new CompApellidos()); System.out.println("LISTA DE PERSONAS"); lisper.forEach(System.out::println); Cosas.ordenar(lisalu, new CompNota()); System.out.println("\nLISTA DE ALUMNOS"); lisalu.forEach(System.out::println); } public static void main(String[] args) { (new Paradigmas3c()).Procesar(); } } |
Aparentemente todo va bien, pero si quisieramos ordenar la lista de alumnos por apellidos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package paradigmas3c; import java.util.*; public class Paradigmas3c { public class Persona { ... } public class Alumno extends Persona { ... } class CompDNI implements Comparator<Persona> { ... } class CompApellidos implements Comparator<Persona> { ... } class CompNota implements Comparator<Alumno> { ... } public void Procesar() { ... Cosas.ordenar(lisper, new CompApellidos()); System.out.println("LISTA DE PERSONAS"); lisper.forEach(System.out::println); Cosas.ordenar(lisalu, new CompApellidos()); System.out.println("\nLISTA DE ALUMNOS"); lisalu.forEach(System.out::println); } ... } |
El compilador nos da un error porque al llamar a ordenar con una lista de alumnos, se tiene que T = Alumno. El comparador que se le proporciona es de clase Comparator<Persona>, que no es compatible con lo que esperaba, un Comparator<Alumno>.
La solución es modificar ligeramente la cabecera de ordenar para indicar que el comparator no tiene que tener como parámetro exactamente la clase T sino que es válida cualquier clase padre de T:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package paradigmas3c; import java.util.*; public class Cosas { public static <T> void ordenar(List<T> lis, Comparator<? super T> comp) { // Ordenación burbuja int n = lis.size(); for(int i = 0; i < n-1; i++) { for(int j = n-1; j > i; j--) { T eant = lis.get(j-1); T eact = lis.get(j); if(comp.compare(eant, eact) > 0) { // lis[j-1] > lis[j] -> Intercambiamos lis.set(j-1, eact); lis.set(j, eant); } } } } } |