Skip to content

7.4 Comparadores

En Java hay dos mecanismos para cambiar la forma en la que los elementos se ordenan.

Imagina que tienes los artículos almacenados en una lista llamada articulos, y que cada artículo se almacena en la siguiente clase Articulo (fíjate que el código de artículo es una cadena y no un número):

1
2
3
4
5
6
7
8
9
class Articulo {
    private String codArticulo; // código de artículo
    private String descripcion; // descripción del artículo
    private int cantidad;       // cantidad a proveer del artículo

    public String getCodArticulo(){ ... }
    public String getDescripcion(){ ... }
    public int getCantidad(){ ... }
}

4.1. Comparator

La primera forma de ordenar consiste en crear una clase que implemente la interfaz java.util.Comparator, y por ende, el método compare definido en dicha interfaz. Esto se explicó en el apartado de conjuntos, al explicar el TreeSet, así que no vamos a profundizar en ello. No obstante, el comparador para ese caso podría ser así:

1
2
3
4
5
6
class comparadorArticulos implements Comparator<Articulo>{
    @Override
    public int compare( Articulo o1, Articulo o2) {
        return o1.getCodArticulo().compareTo( o2.getCodArticulo() );
    }
}

Una vez creada esta clase, ordenar los elementos es muy sencillo; simplemente se pasa como segundo parámetro del método sort una instancia del comparador creado:

Collections.sort(articulos, new comparadorArticulos());

4.2. Comparable

La segunda forma es, quizás, más sencilla cuando se trata de objetos cuya ordenación no existe de forma natural, pero requiere modificar la clase (en el ejemplo la clase Articulo). Consiste en hacer que los objetos que se insertan en la lista o array implementen la interfaz java.util.Comparable. Todos los objetos que implementan la interfaz Comparable son "ordenables" y se puede invocar el método sort sin indicar un comparador para ordenarlos. La interfaz Comparable solo requiere implementar el método compareTo:

1
2
3
4
5
6
7
8
9
class Articulo implements Comparable<Articulo>{
    // atributos ...
    // getters ...

    @Override
    public int compareTo(Articulo o) {
        return this.getCodArticulo().compareTo( o.getCodArticulo() );
    }
}

Del ejemplo anterior se pueden resaltar dos cosas importantes:

1) que la interfaz Comparable es genérica y que para que funcione sin problemas es conveniente indicar el tipo base sobre el que se permite la comparación (en este caso, el objeto Articulo debe compararse consigo mismo), y
2) que el método compareTo solo admite un parámetro, dado que comparará el objeto con el que se pasa por parámetro.

El funcionamiento del método compareTo es el mismo que el método compare de la interfaz Comparator:

  • si la clase que se pasa por parámetro es igual al objeto, se tendría que retornar 0;
  • si es menor o anterior, se debería retornar un número menor que cero;
  • si es mayor o posterior, se debería retornar un número mayor que 0.

Ordenar ahora la lista de artículos es sencillo, fíjate que fácil:

Collections.sort(articulos);
Ejemplo 4.01: Consulta el siguient código

Ejercicio resuelto Comparator1. Imagínate que Objeto es una clase como la siguiente:

package UT07.P4.Comparator1;

public class Objeto {

  public int a;
  public int b;

  public Objeto(int a, int b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public String toString() {
    return "Objeto{" + "a=" + a + ", b=" + b + '}';
  }

}
Imagina que ahora, al añadirlos en un TreeSet, estos se tienen que ordenar de forma que la suma de sus atributos (a y b) sea descendente, ¿como sería el comparador?

Respuesta

Una de las posibles soluciones a este problema podría ser la siguiente:

package UT07.P4.Comparator1;

import java.util.Comparator;

class ComparadorDeObjetos implements Comparator<Objeto> {
  @Override
  public int compare(Objeto o1, Objeto o2) {
     int suma01 = o1.a + o1.b;
     int suma02 = o2.a + o2.b;
     return suma02 - suma01; // invertir la resta para descendientes
  }
}

Y para usarlo tendriamos:

package UT07.P4.Comparator1;

import java.util.TreeSet;

public class Ejemplo11 {

  public static void main(String[] args) {
     TreeSet<Objeto> ts = new TreeSet<Objeto>(new ComparadorDeObjetos());

     Objeto o1= new Objeto(0, 1);
     ts.add(o1);

     ts.add(new Objeto(1, 2));
     ts.add(new Objeto(4, 5));
     ts.add(new Objeto(2, 3));

     for (Objeto elemento : ts) {
        System.out.println(elemento);
     }
  }
}
Observa que la salida muestra los elementos correctamente ordenados, aunque se insertaron de manera "aleatoria":
1
2
3
4
Objeto{a=4, b=5}
Objeto{a=2, b=3}
Objeto{a=1, b=2}
Objeto{a=0, b=1}

Ejemplo 4.02: Consulta el siguient código

Ejercicio resuelto Comparator2. Ahora convertiremos la clase Objeto para que directamente implemente la interfaz Comparable:

package UT07.P4.Comparator2;

public class Objeto implements Comparable<Objeto> {

  public int a;
  public int b;

  public Objeto(int a, int b) {
     this.a = a;
     this.b = b;
  }

  @Override
  public String toString() {
     return "Objeto{" + "a=" + a + ", b=" + b + '}';
  }

  @Override
  public int compareTo(Objeto t) {
     int sumao1 = this.a + this.b;
     int sumao2 = t.a + t.b;
     return suma02 - suma01; // invertir la resta para descendientes
  }

}

Y lo usamos directamente en la clase Principal:

package UT07.P4.Comparator2;

import java.util.TreeSet;

public class Ejemplo12 {

  public static void main(String[] args) {
     TreeSet<Objeto> ts = new TreeSet<Objeto>();

     ts.add(new Objeto(0, 1));
     ts.add(new Objeto(1, 2));
     ts.add(new Objeto(4, 5));
     ts.add(new Objeto(2, 3));

     for (Objeto elemento : ts) {
        System.out.println(elemento);
     }
  }
}

Fíjate que la salida sigue mostrando los elementos correctamente ordenados, aunque se insertaron de manera "aleatoria":

1
2
3
4
Objeto{a=4, b=5}
Objeto{a=2, b=3}
Objeto{a=1, b=2}
Objeto{a=0, b=1}