Quand utiliser Comparable et Comparator

108

J'ai une liste d'objets que je dois trier sur un champ, par exemple Score. Sans trop réfléchir, j'ai écrit une nouvelle classe qui implémente Comparator, qui fait la tâche et ça marche.

En y repensant, je me demande si je devrais avoir à la place l'implémentation de ma classe Comparable au lieu de créer une nouvelle classe qui implémente Comparator. Le score est le seul champ sur lequel les objets seront classés.

  1. Qu'est-ce que j'ai fait comme pratique acceptable?

  2. La bonne approche est-elle «Commencez par implémenter la classe Comparable (pour l'ordre naturel) et si une comparaison de champ alternative est requise, créez une nouvelle classe qui implémente Comparator»?

  3. Si (2) ci-dessus est vrai, cela signifie-t-il que l'on ne doit implémenter Comparator qu'après avoir implémenté la classe Comparable? (En supposant que je possède la classe d'origine).

pkrish
la source

Réponses:

80

Je dirais qu'un objet devrait implémenter Comparable si c'est la manière naturelle et claire de trier la classe, et quiconque aurait besoin de trier la classe voudrait généralement le faire de cette façon.

Si, cependant, le tri était une utilisation inhabituelle de la classe, ou si le tri n'a de sens que pour un cas d'utilisation spécifique, alors un comparateur est une meilleure option.

En d'autres termes, étant donné le nom de la classe, est-il clair comment un comparable trierait, ou devez-vous recourir à la lecture du javadoc? Si c'est le dernier cas, il est probable que chaque cas d'utilisation du tri futur nécessiterait un comparateur, auquel cas la mise en œuvre de comparables peut ralentir les utilisateurs de la classe, pas les accélérer.

Yishai
la source
Pouvez-vous donner un exemple rapide?
rgamber
cela pourrait être un bon exemple: gist.github.com/yclian/2627608 Il existe une classe Version qui utilise ComparableVersion. Version - fournit des méthodes d'usine ComparableVersion supposées être object (sans méthodes statiques) - fournit une version qui peut être comparée à une autre. Les responsabilités sont séparées.
ses
2
vous pouvez vous référer à java-journal.blogspot.in/2011/01/…
un apprenant le
L'intervieweur a demandé, pourquoi utiliser Comparator alors que la même chose peut être faite avec Comparable, et j'étais stupide :(
Aadam
@aLearner link is dead
G.Brown
127

À utiliser Comparablesi vous souhaitez définir un comportement de tri par défaut (naturel) de l'objet en question, une pratique courante consiste à utiliser un identifiant technique ou naturel (base de données?) De l'objet pour cela.

À utiliser Comparatorsi vous souhaitez définir un comportement de classement contrôlable externe , cela peut remplacer le comportement de classement par défaut.

BalusC
la source
3
C'est une explication technique, aussi correcte que possible, mais elle ne dit vraiment rien sur les meilleures pratiques.
extraneon le
40
c'est dire quand utiliser chacun - si ce n'est pas une bonne pratique, qu'est-ce que c'est?
Bozho
1
"Est-ce que la mise en œuvre Comparablesignifie que je définis l'ordre naturel?" , cela m'a donné la réponse que je cherchais. Merci :)
Somjit
61

Utilisez Comparable:

  • si l'objet est sous votre contrôle.
  • si le comportement de comparaison est le comportement de comparaison principal.

Utilisez Comparator:

  • si l'objet est hors de votre contrôle et que vous ne pouvez pas les faire implémenter Comparable.
  • lorsque vous souhaitez comparer un comportement différent du comportement par défaut (qui est spécifié par Comparable).
Bozho
la source
20

Comparable -java.lang.Comparable: int compareTo(Object o1)

Un objet comparable est capable de se comparer à un autre objet. La classe elle-même doit implémenter l'interface java.lang.Comparable afin de pouvoir comparer ses instances.

  • Capable de comparer l'objet actuel avec l'objet fourni.
  • En utilisant cela, nous pouvons implémenter en only one sort sequencefonction des propriétés des instances. EX:Person.id
  • Certaines des classes prédéfinies telles que String, Wrapper classes, Date, Calendar ont implémenté une interface Comparable.

Comparateur -java.util.Comparator: int compare(Object o1, Object o2)

Un objet comparateur est capable de comparer deux objets différents. La classe ne compare pas ses instances, mais certaines autres instances de la classe. Cette classe de comparateur doit implémenter l'interface java.util.Comparator.

  • Capable de comparer deux objets du même type.
  • En utilisant cela, nous pouvons implémenter many sort sequenceet nommer chacun, en fonction des propriétés des instances. EX:Person.id, Person.name, Person.age
  • Nous pouvons implémenter l'interface Comparator à nos classes prédéfinies pour un tri personnalisé.

Exemple:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • Pour un tri personnalisé, nous optons pour le comparateur @compare (o1, o2) pour d'autres scénarios, nous optons pour un @compareTo (o1) comparable, sans changer de code si nous voulons trier plus d'un champ, nous utilisons le comparateur.

Pour Java 8 Lambda: Comparator, reportez-vous à mon article.

Yash
la source
10

Comparable doit être utilisé lorsque vous comparez des instances de la même classe.

Le comparateur peut être utilisé pour comparer des instances de différentes classes.

Comparable est implémenté par la classe qui doit définir un ordre naturel pour ses objets. Like String implémente Comparable.

Dans le cas où l'on souhaite un ordre de tri différent, il peut implémenter le comparateur et définir sa propre manière de comparer deux instances.

Devang Paliwal
la source
10

Si le tri des objets doit être basé sur l'ordre naturel, utilisez Comparable alors que si votre tri doit être effectué sur les attributs de différents objets, utilisez Comparator en Java.

Principales différences entre Comparable et Comparator:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+
Joby Wilson Mathews
la source
9

Comparator fait tout ce qui est comparable, et plus encore.

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

J'ai trouvé la meilleure approche pour utiliser les comparateurs en tant que classes anonymes comme suit:

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

Vous pouvez créer plusieurs versions de ces méthodes directement dans la classe que vous prévoyez de trier. Vous pouvez donc avoir:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    etc...

Maintenant, vous pouvez utiliser ces méthodes de tri n'importe où et obtenir la réutilisation du code. Cela me donne tout ce qui serait comparable, et plus encore ... donc je ne vois aucune raison d'utiliser comparable.


la source
8

Je dirais:

  • si la comparaison est intuitive, alors implémentez par tous les moyens Comparable
  • si vous ne savez pas si votre comparaison est intuitive, utilisez un comparateur car il est plus explicite et donc plus clair pour la pauvre âme qui doit maintenir le code
  • s'il y a plus d'une comparaison intuitive possible, je préférerais un comparateur, éventuellement construit par une méthode d'usine dans la classe à comparer.
  • si la comparaison est un usage spécial, utilisez Comparator
extraneon
la source
6

Les points suivants vous aident à décider dans quelles situations utiliser Comparable et dans quel comparateur:

1) Disponibilité du code

2) Critères de tri simples ou multiples

3) Arays.sort () et Collection.sort ()

4) En tant que clés dans SortedMap et SortedSet

5) Plus de nombre de classes par rapport à la flexibilité

6) Comparaisons interclasses

7) Ordre naturel

Pour un article plus détaillé, vous pouvez vous référer Quand utiliser comparable et quand utiliser le comparateur

un apprenant
la source
Je me demande pourquoi personne ne vote pour cette réponse. C'est vraiment sympa. +1
Diganta
4
  • Si au moment de la rédaction de la classe, vous n'avez qu'un seul cas d'utilisation du tri, utilisez Comparable.
  • Uniquement lorsque vous avez plus d'une stratégie de tri, implémentez un comparateur.
fabrizioM
la source
4

Si vous avez besoin d'un tri par ordre naturel - Comparable par l'utilisateur SI vous avez besoin d'un tri par ordre personnalisé - Utilisez le comparateur

Exemple:

Class Employee{
private int id;
private String name;
private String department;
}

Ordre naturel Le tri serait basé sur l'identifiant car il serait unique et l'ordre personnalisé serait le nom et le département.

Réfrences:
Quand une classe doit-elle être comparable et / ou comparative? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

Rajiv
la source
3

Il y avait eu une question similaire ici: Quand une classe devrait-elle être comparable et / ou comparative?

Je dirais ce qui suit: implémenter Comparable pour quelque chose comme un ordre naturel, par exemple basé sur un identifiant interne

Implémentez un comparateur si vous avez un algorithme de comparaison plus complexe, par exemple plusieurs champs, etc.

ZeissS
la source
1
La commande sur plusieurs champs peut être aussi bien faite avec Comparable.
BalusC
Pour connaître la
a Learner le
2

Comparable:
chaque fois que nous voulons stocker uniquement des éléments homogènes et l'ordre de tri naturel par défaut requis, nous pouvons opter pour une comparableinterface d' implémentation de classe .

Comparateur:
Chaque fois que nous voulons stocker des éléments homogènes et hétérogènes et que nous voulons trier dans l'ordre de tri personnalisé par défaut, nous pouvons opter pour l' comparatorinterface.

G swamy reddy
la source
0

Mon besoin était trié en fonction de la date.

Donc, j'ai utilisé Comparable et cela a fonctionné facilement pour moi.

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

Une restriction avec Comparable est qu'ils ne peuvent pas être utilisés pour des collections autres que List.

printemps pro
la source
0

Si vous possédez la classe, mieux vaut opter pour Comparable . Généralement, Comparator est utilisé si vous ne possédez pas la classe mais que vous devez l'utiliser un TreeSet ou TreeMap car Comparator peut être passé en paramètre dans le conctructor de TreeSet ou TreeMap. Vous pouvez voir comment utiliser Comparator et Comparable sur http://precatelyconcise.com/java/collections/g_comparator.php

Sai Sunder
la source
0

On m'a demandé de trier une gamme définie de nombres en temps meilleur que nlogn dans une interview. (Ne pas utiliser le tri par comptage)

L'implémentation de l'interface Comparable sur un objet permet aux algos de tri implicites d'utiliser la méthode compareTo remplacée pour ordonner les éléments de tri et ce serait un temps linéaire.

M. merveilleux
la source
0

Comparable est l'ordre de tri naturel par défaut fourni pour les valeurs numériques par ordre croissant et pour les chaînes par ordre alphabétique. par exemple:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Le comparateur est l'ordre de tri personnalisé implémenté dans la classe myComparator personnalisée en remplaçant une méthode de comparaison par exemple:

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]
Ajay Takur
la source
-1

Une approche très simple consiste à supposer que la classe d'entité en question soit représentée dans la base de données, puis dans la table de base de données auriez-vous besoin d'un index composé de champs de la classe d'entité? Si la réponse est oui, implémentez comparable et utilisez le ou les champs d'index pour l'ordre de tri naturel. Dans tous les autres cas, utilisez le comparateur.

user976715
la source
-2

Ma bibliothèque d'annotations pour l'implémentation Comparableet Comparator:

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

Cliquez sur le lien pour voir plus d'exemples. http://code.google.com/p/compamatic/wiki/CompamaticByExamples

Jianmin Liu
la source