Les comparateurs en Java

Comment Comparer puis trier une liste (ArrayList ou List) d'objets en Java en utilisant les interfaces Comparator et Comparable ?

Using Comparator and Comparable interfaces

Published by: Stephane, Last edited

Dans cet article nous verrons comment utiliser les interfaces Comparator et Comparable avec la méthode sort() de la classe Collections pour trier les objets d'une liste (ArrayList ou List) dans un ordre croissant ou décroissant à partir d’un ou de plusieurs critère(s) déterminé(s). On verra également la différence entre ces deux interfaces et quand utiliser l’une ou l’autre.

Problème : Dans notre salle de classe nous avons des étudiants que nous désirons classer par ordre d’âge croissant. Comment si prendre ? Nous aimerions également par la suite les classer suivant leur moyenne annuelle et leur nom Que faire ?

A. Utilisation de l’interface Comparable pour comparer suivant un seul critère


L’interface comparable facilite la comparaison des objets suivant un critère connue d’avance. Dans notre cas on désire comparer les étudiants suivant leur âge.

Cette interface a une seule méthode abstraite compareTo() qui accepte un objet en paramètre et retourne un nombre entier (int).


@Override
public int compareTo(Etudiant etudiant) {
    if(etudiant == null) // Linge #n1
        return -1;
    return this.age - etudiant.age; // Linge #n2
}

La première étape (ligne #n1) consiste à s’assurer que l’objet reçu en paramètre n’est pas null.

La deuxième étape c’est la comparaison à proprement parlé (ligne #n2). Pour cela on fait la différence d’âge entre l’étudiant actuel (this) et celui reçu en paramètre. En effectuant la différence dans cet ordre on obtient un classement par ordre d’âge croissant et ainsi, si l’âge de l’étudiant actuel (this) est supérieur à celui de l’étudiant reçu en paramètre un nombre positif est retourné signifiant que l’âge de l’étudiant actuel est supérieur à celui reçu en paramètre. Un nombre négatif sera retourné dans le cas contraire signifiant que l’étudiant reçu en paramètre à un âge supérieur à celui de l’étudiant actuel (this). Si on désire obtenir ce classement par ordre d’âge décroissant on peut effectuer une différence inverse :

return etudiant.age – this.age;

Contrairement à l’interface Comparator qu’on verra plus bas, l’interface Comparable est faite pour être implémentée par la classe dont on veut effectuer les comparaisons.

Limite de l’interface Comparable

Cette interface est peu flexible car elle limite la comparaison suivant un seul critère connu au préalable. Qu’en sera-t-il lorsque nous désirerons comparer les mêmes élèves suivant leur moyenne annuelle ou suivant l’ordre alphabétique du nom ? ça devient compliqué n’est-ce pas ?

Comparator au secours !!! ☺.

B. Utilisation de l’interface Comparator pour comparer suivant différents critères


Explication

L’interface Comparator contient également une seule méthode abstraite compare() qui accepte deux objets à comparer en paramètre.

Cette interface n’est pas implémentée par la classe à comparer et peut être utilisée de différentes manières.

  1. Utilisation d’une classe anonyme (Anonymous inner class): dans ce cas on va créer une instance de cet interface (new) et directement implémenter la méthode compare(). C’est ce qui est fait à partir de la ligne #n3. Dans un prochain article on parlera des classes imbriquée (nested classes) et des classes anonymes plus en détail.
    
    // Ligne #n3 Utilisation d'une classe anonyme
    Comparator comparerParMoyenne = new Comparator() {
        @Override
        public int compare(Etudiant etudiant1, Etudiant etudiant2) {
            return etudiant1.getMoyenneAnnuelle() - etudiant2.getMoyenneAnnuelle();
        }
    
    };
                     
  2. Utilisation d’une fonction lambda (<=Java 8): Vu que l’interface Comparator contient une seule méthode abstraite, compare(), cette interface est dite fonctionnelle. De ce fait on peut utiliser une fonction lambda pour créer une instance de cette interface. C’est ce qui est fait à partir de la ligne #n4. Dans un prochain article nous parlerons des fonctions lambda un peu plus en profondeur.
    
    //Ligne #n5. Utilisation d'une fonction lambda et delegation
    //de la comparaison à la methode compareTo de la classe String
    Comparator comparerParNom =
            (etudiant1, etudiant2) -> etudiant1.getPrenom().compareTo(etudiant2.getPrenom());
                     
  3. Implémenter l’interface par une classe spécialement faite pour la comparaison: Cette méthode rend le code un peu plus long et pas très lisible. Je conseille donc l’utilisation de la méthode 1. si vous écrivez du code sous java 7 en descendant et la méthode 2. si vous écrivez sous java 8 et plus.

Conclusion

Nous sommes arrivé au terme de cet article où nous avons vu comment comparer et trier des objet en Java en utilisation les interfaces Comparable et Comparator en plus de la méthode sort() de la classe Collections. J’espère que vous l’avez trouvé intéressant et qu’il vous aidera à écrire du code meilleur.

Si vous avez des questions ou commentaires n’hésitez pas à me contacter sur twitter ou LinkedIn. Merci de partager avec d’autres personnes si vous l’avez trouvé intéressant.

Closing Tip: Il est vivement encouragé qu'à chaque fois que vous implémentez l’interface comparable, qu’il y ait une consistance entre les méthodes equal() et compareTo(). En d’autre terme il faut s’assurer qu’à chaque fois que x.equals(y) retourne “true”, que x.compareTo(y) retourne 0.

Find me on twitter or on

"Je puis tout par Christ qui me fortifie." Phil 4:13