L'interface Comparator permet de réaliser une comparaison qui impose un ordre global sur les éléments d'une collection. Les comparateurs peuvent être passés à une méthode de tri, telle que Collections.sort() ou Arrays.sort(), dans le but d'appliquer un contrôle précis sur l'ordre de tri. Les comparateurs peuvent aussi être utilisés pour contrôler l'ordre de certaines structures de données, telles que les objets TreeSet et TreeMap.
L'ordre imposé par un objet Comparator sur un ensemble d'éléments est dit être consistant avec equals() si et seulement si (compare((Object)e1, (Object)e2)==0) retourne la même valeur booléenne que l'expression e1.equals((Object)e2) pour chaque e1 et e2 dans l'ensemble précité.
Cependant des précautions devraient être prises, lors de l'utilisation d'un comparateur capable d'imposer un ordre inconsistant avec equals() ordonnant une collection TreeSet ou TreeMap. En supposant qu'un ensemble SortedSet ou SortedMap avec un comparateur explicite sont utilisés avec les éléments ou les clés extraits d'un ensemble, l'ordre imposé par le comparateur sur l'ensemble précité est inconsistant avec equals() et la collection triée se comportera d'une manière étrange. En particulier, elle violerait le contrat général des collections Set ou Map, lesquelles sont définies en terme d'égalité.
Par exemple, si l'ajout de deux clés a et b, tel que (a.equals((Object)b) && c.compare((Object)a, (Object)b) != 0) dans un ensemble SortedSet avec un comparateur, la seconde opération d'ajout retournerait la valeur booléenne false, car les deux clés sont équivalentes à partir de la perspective de l'objet SortedSet.
Il est approprié que les comparateurs implémentent l'interface java.io.Serializable, étant donné qu'ils peuvent être utilisés comme méthode d'ordonnancement dans des structures de données sérialisables, à l'image des objets Treeset et TreeMap. Les structures de données correctement ordonnées pourront être sérialiser avec succès, c'est pourquoi, les comparateurs doivent implémenter l'interface Serializable.
La relation qui définit l'ordre global qu'un comparateur donné impose à un ensemble d'objets S est :
{(x, y) tel que c.compare((Object)x, (Object)y) <= 0}
Le quotient pour l'ordre total est :
{(x, y) tel que c.compare((Object)x, (Object)y) == 0}
Cela découle directement du contrat pour la méthode compare(), que le quotient soit une relation d'équivalence sur S et que l'ordre naturel soit un ordre global sur S. Lorsque l'ordre imposé par la comparateur sur l'ensemble est dit consistant avec equals(), cela signifie que le quotient de l'ordre naturel est la relation d'équivalence définie par les méthodes equals() des objets.
{(x, y) tel que x.equals((Object)y)}
Le seul constructeur Comparator() s'utilise avec une classe anonyme chargée de définir la méthode comparaison.
Comparator comparateur = new Comparator() { public int compare(Object o1, Object o2){ // instructions... } };
Les méthodes |
---|
int compare(Object o1, Object o2) |
compare les deux objets pour indiquer leur ordre. |
boolean equals(Object obj) |
indique si l'objet spécifié est égal à l'implémentation de l'interface Comparator. |
La méthode compare() retourne une valeur entière indiquant l'ordre des objets soumis.
int res = comparateur.compare(arg1, arg2);
L'implémenteur doit s'assurer des règles suivantes :
Bien que la dernière règle ne soit pas strictement requise, il est fortement recommandée de l'appliquer.
La méthode equals() doit retourner la valeur true, si et seulement si l'objet spécifié est aussi un comparateur et qu'il impose le même ordre que l'objet Comparator courant. Ainsi, comp1.equals(comp2) implique que sgn(comp1.compare(obj1, obj2)) == sgn(comp2.compare(obj1, obj2)) pour chaque référence d'objet obj1 et obj2.
Comparator comparateur1 = new Comparateur(); Comparator comparateur2 = new AutreComparateur(); boolean res = comparateur1.equals(comparateur2);
La notation sgn(expression) désigne la fonction mathématique signum(), laquelle est définie pour retourner les valeurs -1, 0 et 1 pour respectivement toutes les valeurs négatives, nulles et positives renvoyées par l'expression.
Exemple [voir]import java.util.Comparator; import java.util.TreeSet; public class CollectionComparator { static final Comparator ORDRE_DATE = new Comparator() { public int compare(Object o1, Object o2){ if(!(o1 instanceof Video)) throw new ClassCastException(); return (new Integer(((Video)o1).obtenirAnnee())).compareTo( new Integer(((Video)o2).obtenirAnnee())); } }; static final Comparator ORDRE_REALISATEUR = new Comparator() { public int compare(Object o1, Object o2){ if(!(o1 instanceof Video)) throw new ClassCastException(); return (((Video)o1).obtenirRealisateur()).compareTo( ((Video)o2).obtenirRealisateur()); } }; public static void main(String[] args) { TreeSet t = new TreeSet(); t.add(new Video("Le jour le plus long", "Ken Annakin", 1962)); t.add(new Video("Un pont trop loin", "Richard Attenborough", 1977)); t.add(new Video("Platoon", "Oliver Stone", 1986)); t.add(new Video("Full metal jacket", "Stanley Kubrik", 1987)); t.add(new Video("La ligne rouge", "Terrence Malick", 1998)); t.add(new Video("The patriot", "Roland Emmerich", 2000)); System.out.println("Tri par titre :\n" + t); TreeSet tr = new TreeSet(ORDRE_REALISATEUR); tr.addAll(t); System.out.println("\nTri par réalisateur :\n" + tr); TreeSet ta = new TreeSet(ORDRE_DATE); ta.addAll(t); System.out.println("\nTri par année :\n" + ta); } } //Classe Video implémentant l'interface java.lang.Comparable import java.util.GregorianCalendar; import java.util.Calendar; public class Video implements Comparable { private String titre, realisateur; private int annee; public Video(){ this("Inconnu", "Inconnu", 0); } public Video (String[] infos) { this(infos[0], infos[1], Integer.parseInt(infos[3])); } public Video (String titre, String realisateur){ this(titre, realisateur, (new GregorianCalendar()).get(Calendar.YEAR)); } public Video (String titre, String realisateur, String annee){ this(titre, realisateur, Integer.parseInt(annee)); } public Video (String titre, String realisateur, int annee){ if (titre == null || realisateur == null) throw new NullPointerException(); this.titre = titre; this.realisateur = realisateur; this.annee = annee; } public String obtenirTitre(){ return this.titre; } public String obtenirRealisateur(){ return this.realisateur; } public int obtenirAnnee(){ return this.annee; } public void fournirTitre(String titre){ this.titre = titre; } public void fournirRealisateur(String realisateur){ this.realisateur = realisateur; } public void fournirAnnee(int annee){ this.annee = annee; } public int hashCode(){ return annee * titre.hashCode() + realisateur.hashCode(); } public boolean equals(Object o){ if(!(o instanceof Video)) return false; Video v = (Video)o; if(this.hashCode() == v.hashCode()) return true; return false; } public int compareTo(Object o){ if(!(o instanceof Video)) throw new ClassCastException(); Video v = (Video)o; int comparaison; if((comparaison = titre.compareTo(v.obtenirTitre())) != 0) return comparaison; else if((comparaison = realisateur.compareTo(v.obtenirRealisateur())) != 0) return comparaison; else return (new Integer(annee)).compareTo(new Integer(v.obtenirAnnee())); } public String toString(){ StringBuffer res = new StringBuffer("["); res.append(titre); res.append(", "); res.append(realisateur); res.append(", "); res.append(annee); return res.append("]").toString(); } } |