Artículos de Tecnología

Ordenando colecciones con Comparable y Comparator

steppat
steppat

Introducción

Una tarea común en el día a día de los desarrolladores es ordenar una lista o array. Para no reinventar la rueda, la API Collections de Java (también conocida por el nombre de su paquete, el java.util) viene preparada para ayudar en esta tarea. En este articulo voy a pasar al problema específico de ordenación.

Ordenación

Este post fue creado en 2010 y fue actualizado en este año de 2023.

Imaginemos que queremos ordenar una lista de Cuentas bancarias. Cada cuenta posee un número (int) y un titular (String):

Cuenta cuenta1 = new Cuenta(5452, "Phillip Lahm");
Cuenta conta2 = new Cuenta(1234, "Lucas Podolski");
Conta cuenta3 = new Cuenta(3145, "Arne Friedrich");

List lista = new ArrayList();
lista.add(cuenta1);
lista.add(cuenta2);
lista.add(cuenta3);

El método para ordenar una lista se encuentra en la clase java.util.Collections (fíjate en el "s" al final). Ella posee métodos estáticos que ayudan a manipular colecciones, entre ellos el método sort. Así podemos intentar ordenar la lista de cuentas:

Collections.sort(lista);

Pero, por desgracia, la línea anterior ni siquiera compila. Antes de invocar el método sort es preciso definir el criterio de ordenación: una forma de informar, dado dos cuentas, cuál viene "antes" y cuál viene "después".

Considerando que queremos ordenar por el número de la cuenta, la clase Conta debe implementar la interfaz java.lang.Comparable que define lo que será nuestra "orden natural". La interfaz posee solo un método compareTo:

public interface Comparable {
    int compareTo(T otro);
}

Las cuentas entonces deben ser comparables. Vamos a definir la orden natural basada en el número de la cuenta:

public class Conta implements Comparable {

    private int numero;
    private String titular;
    // otros metodos y atributos

    public int compareTo(Cuenta outraConta) {
        if (this.numero < otraCuenta.numero) {
            return -1;
        }
        if (this.numero > otraCuenta.numero) {
            return 1;
        }
        return 0;
    }
}

Si el número de la cuenta actual es menor que el de otraCuenta, retornamos -1 (o cualquier int negativo, indicando que this debe venir "antes" de otraCuenta), si es mayor retornamos 1 (o cualquier int positivo) y si es igual entonces devolvemos 0.

Ahora podemos invocar Collections.sort(lista).

Pero, ¿y si surge la necesidad de ordenar por el titular de la cuenta? No queremos alterar el método compareTo en la clase Cuenta, ya que eso cambiaría la orden natural. Queremos definir otro criterio de ordenación. Para ello, existe otra interfaz: la Comparator:

public interface Comparator {
    int compare(T o1, T o2);
}

Vamos entonces a implementar la interfaz para definir la orden por el titular (String) de la cuenta. Comparar dos Strings es difícil, pero, como puedes imaginar, ese problema ya ha sido resuelto en la API de Java.

La clase String ya sabe comparar dos strings, lo sabemos porque ella implementa la interfaz Comparable (por ese mismo motivo podemos llamar Collections.sort para una List de String). Podemos entonces delegar esa tarea al método compareTo de las Strings:

public class TitularComparator implements Comparator {
    public int compare(Cuenta cuenta, Cuenta otraCuenta) {
        return cuenta.getTitular().
                compareTo(otraCuenta.getTitular());
    }
}

¿Cómo elegir para que ese criterio de comparación sea utilizado en vez de la orden natural? El método sort es sobrecargado y puede recibir un objeto del tipo Comparator:

TitularComparator comparator = new TitularComparator();
Collections.sort(lista, comparator);

Falta mencionar que el método compareTo de la interfaz Comparable debe ser consistente con el método equals. Cuando una cuenta es igual a otra (la clase Conta sobrescribe el método equals para definir igualdad), el método compareTo debe devolver cero también. Debemos también pensar si recibiremos null como Cuenta para ordenar, y tomar las debidas precauciones en los comparadores.

La ordenación de la colección TreeSet y las claves del mapa TreeMap también se controla a través de Comparable y Comparator.

Vale la pena recordar que muchas cosas han cambiado desde esta publicación, con la introducción de Java 8, y puede leer sobre lambdas y otras características que ayudan en funciones como comparación en el blog de Alura Latam.

Artículos de Tecnología

En Alura encontrarás variados cursos sobre . ¡Comienza ahora!

Precios en:
USD
  • USD
  • BOB
  • CLP
  • COP
  • USD
  • PEN
  • MXN
  • UYU

Semestral

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana

  • Acceso a todo el contenido de la plataforma por 6 meses
US$ 65.90
un solo pago de US$ 65.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Anual

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana

  • Acceso a todo el contenido de la plataforma por 12 meses
US$ 99.90
un solo pago de US$ 99.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Acceso a todos
los cursos

Estudia las 24 horas,
dónde y cuándo quieras

Nuevos cursos
cada semana