Quando utilizzare Comparable e Comparator


108

Ho un elenco di oggetti che devo ordinare in un campo, ad esempio Punteggio. Senza pensarci troppo ho scritto una nuova classe che implementa Comparator, che fa il compito e funziona.

Ora, guardando indietro a questo, mi chiedo se avrei dovuto invece che la mia classe implementasse Comparable invece di creare una nuova classe che implementa Comparator. Il punteggio è l'unico campo in cui verranno ordinati gli oggetti.

  1. Quello che ho fatto è accettabile come pratica?

  2. L'approccio giusto è "Per prima cosa la classe implementa Comparable (per l'ordinamento naturale) e se è richiesto un confronto di campi alternativo, creare una nuova classe che implementa Comparator"?

  3. Se la (2) sopra è vera, significa che si dovrebbe implementare Comparator solo dopo che la classe ha implementato Comparable? (Supponendo che io possieda la classe originale).

Risposte:


80

Direi che un oggetto dovrebbe implementare Comparable se questo è il modo chiaro e naturale per ordinare la classe, e chiunque avrebbe bisogno di ordinare la classe in genere vorrebbe farlo in questo modo.

Se, tuttavia, l'ordinamento era un uso insolito della classe, o l'ordinamento ha senso solo per un caso d'uso specifico, allora un Comparatore è un'opzione migliore.

In altre parole, dato il nome della classe, è chiaro come si ordina un paragonabile o devi ricorrere alla lettura del javadoc? In quest'ultimo caso, le probabilità sono che ogni caso d'uso futuro di ordinamento richiederebbe un comparatore, a quel punto l'implementazione di comparable potrebbe rallentare gli utenti della classe, non velocizzarli.


Puoi fare un rapido esempio?
rgamber

questo potrebbe essere un buon esempio: gist.github.com/yclian/2627608 C'è la classe Version che usa ComparableVersion. Versione - fornisce metodi di fabbrica ComparableVersion supposti per oggetto (senza metodi statici) - fornisce una versione che può essere confrontata con un'altra. Le responsabilità sono separate.
ses


L'intervistatore ha chiesto, perché usare Comparator quando lo stesso può essere fatto con Comparable, e sono rimasto stupido :(
Aadam

@aLearner link is dead
G.Brown

127

Utilizzare Comparablese si desidera definire un comportamento di ordinamento predefinito (naturale) dell'oggetto in questione, una pratica comune è utilizzare un identificatore tecnico o naturale (database?) Dell'oggetto per questo.

Utilizzare Comparatorse si desidera definire un comportamento di ordinamento controllabile esterno , questo può sovrascrivere il comportamento di ordinamento predefinito.


3
Questa è una spiegazione tecnica e per quanto corretta, ma in realtà non dice nulla sulle migliori pratiche.
extraneon

40
sta dicendo quando usarli - se questa non è una best practice, che cos'è?
Bozho

1
"L'implementazione Comparablesignifica che sto definendo l'ordine naturale?" , questo mi ha dato la risposta che stavo cercando. Grazie :)
Somjit

61

Usa Comparable:

  • se l'oggetto è sotto il tuo controllo.
  • se il comportamento di confronto è il principale comportamento di confronto.

Usa Comparator:

  • se l'oggetto è fuori dal tuo controllo e non puoi farli implementare Comparable .
  • quando si desidera confrontare un comportamento diverso da quello predefinito (specificato da Comparable ).

20

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

Un oggetto comparabile è in grado di confrontarsi con un altro oggetto. La classe stessa deve implementare l'interfaccia java.lang.Comparable per poter confrontare le sue istanze.

  • Capace di confrontare l'oggetto corrente con l'oggetto fornito.
  • Usando questo possiamo implementare only one sort sequence base alle proprietà delle istanze. EX:Person.id
  • Alcune delle classi predefinite come String, classi wrapper, Date, Calendar hanno implementato l'interfaccia Comparable.

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

Un oggetto comparatore è in grado di confrontare due oggetti diversi. La classe non confronta le sue istanze, ma le istanze di un'altra classe. Questa classe di confronto deve implementare l'interfaccia java.util.Comparator.

  • Capace di confrontare due oggetti dello stesso tipo.
  • Usando questo possiamo implementare many sort sequencee nominare ciascuno, in base alle proprietà delle istanze. EX:Person.id, Person.name, Person.age
  • Possiamo implementare l'interfaccia di confronto nelle nostre classi predefinite per l'ordinamento personalizzato.

Esempio:

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)
    }
}
  • Per l'ordinamento personalizzato utilizziamo il comparatore @compare (o1, o2) per altri scenari utilizziamo il comparabile @compareTo (o1), senza modificare il codice se vogliamo ordinare più di un campo, quindi utilizziamo il comparatore.

Per Java 8 Lambda: il comparatore fare riferimento al mio post.


10

Comparable dovrebbe essere utilizzato quando si confrontano istanze della stessa classe.

Il comparatore può essere utilizzato per confrontare istanze di classi diverse.

Comparable è implementato dalla classe che ha bisogno di definire un ordinamento naturale per i suoi oggetti. Like String implementa Comparable.

Nel caso in cui si desideri un diverso ordinamento, è possibile implementare il comparatore e definire il proprio modo di confrontare due istanze.


10

Se l'ordinamento degli oggetti deve essere basato sull'ordine naturale, utilizzare Comparable, mentre se l'ordinamento deve essere eseguito su attributi di oggetti diversi, utilizzare Comparator in Java.

Principali differenze tra Comparable e 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.        ¦
+------------------------------------------------------------------------------------+

9

Il comparatore fa tutto ciò che fa il comparabile, e molto altro ancora.

| | 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

Ho trovato l'approccio migliore per utilizzare i comparatori come classi anonime come segue:

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());
        }
    });
}

Puoi creare più versioni di tali metodi direttamente nella classe che intendi ordinare. Quindi puoi avere:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    eccetera...

Ora puoi utilizzare questi metodi di ordinamento ovunque e ottenere il riutilizzo del codice. Questo mi dà tutto ciò che sarebbe comparabile, e molto altro ... quindi non vedo alcun motivo per usare comparabile.


8

Direi:

  • se il confronto è intuitivo, implementa assolutamente Comparable
  • se non è chiaro se il tuo confronto sia intuitivo, usa un Comparatore in quanto è più esplicito e quindi più chiaro per la povera anima che deve mantenere il codice
  • se è possibile più di un confronto intuitivo, preferirei un comparatore, possibilmente costruito da un metodo di fabbrica nella classe da confrontare.
  • se il confronto è per scopi speciali, utilizzare Comparator

6

I seguenti punti ti aiutano a decidere in quali situazioni utilizzare Comparable e in quale Comparator:

1) Disponibilità del codice

2) Criteri di ordinamento singoli e multipli

3) Arays.sort () e Collection.sort ()

4) Come chiavi in ​​SortedMap e SortedSet

5) Più numero di classi contro flessibilità

6) Confronti tra classi

7) Ordine naturale

Per un articolo più dettagliato puoi fare riferimento a Quando usare comparable e quando usare comparator


Mi chiedo perché nessuno stia votando questa risposta. È davvero carino. +1
Diganta

4
  • Se al momento della scrittura della classe hai un solo caso d'uso di ordinamento usa Comparable.
  • Solo quando hai più di una strategia di ordinamento implementa un comparatore.

4

Se hai bisogno di un ordinamento naturale degli ordini - Comparabile dell'utente SE hai bisogno di un ordinamento degli ordini personalizzato - Usa il comparatore

Esempio:

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

L'ordinamento per ordine naturale sarebbe basato sull'id perché sarebbe univoco e l'ordinamento per ordine personalizzato sarebbe nome e reparto.

Refrences:
quando una classe dovrebbe essere Comparable e / o Comparator? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html


3

C'era stata una domanda simile qui: quando una classe dovrebbe essere comparabile e / o comparatrice?

Direi quanto segue: Implement Comparable per qualcosa come un ordinamento naturale, ad esempio basato su un ID interno

Implementa un comparatore se hai un algoritmo di confronto più complesso, ad esempio più campi e così via.


1
Ordinare su più campi può essere altrettanto utile Comparable.
BalusC

Per la differenza tra comparable e comparator puoi fare riferimento a java-journal.blogspot.in/2010/12/…
a Learner

2

Comparabile:
ogni volta che vogliamo memorizzare solo elementi omogenei e l'ordine di ordinamento naturale predefinito richiesto, possiamo optare per l'implementazione della classe comparable interfaccia di .

Comparatore:
ogni volta che vogliamo memorizzare elementi omogenei ed eterogenei e vogliamo ordinare in base all'ordinamento personalizzato predefinito, possiamo optare per l' comparatorinterfaccia.


0

Il mio bisogno era ordinato in base alla data.

Quindi, ho usato Comparable e ha funzionato facilmente per me.

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

Una restrizione con Comparable è che non possono essere utilizzati per raccolte diverse da List.


0

Se sei il proprietario della classe, meglio andare con Comparable . Generalmente Comparator viene utilizzato se non si possiede la classe ma è necessario utilizzarla come TreeSet o TreeMap perché Comparator può essere passato come parametro nel costruttore di TreeSet o TreeMap. Puoi vedere come usare Comparator e Comparable in http://preciselyconcise.com/java/collections/g_comparator.php


0

Mi è stato chiesto di ordinare una gamma definita di numeri in un tempo migliore del nlogn in una delle interviste. (Non si utilizza l'ordinamento conteggio)

L'implementazione dell'interfaccia Comparable su un oggetto consente agli algoritmi di ordinamento impliciti di utilizzare il metodo compareTo sovrascritto per ordinare gli elementi di ordinamento e questo sarebbe tempo lineare.


0

Comparable è l'ordinamento naturale predefinito fornito per i valori numerici crescente e per le stringhe in ordine alfabetico. per esempio:

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

Il comparatore è l'ordinamento personalizzato implementato nella classe personalizzata myComparator sovrascrivendo un metodo di confronto per es:

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]

-1

Un approccio molto semplice consiste nel presumere che la classe di entità in questione sia rappresentata nel database e quindi nella tabella del database avresti bisogno di un indice composto da campi della classe di entità? Se la risposta è sì, implementare comparable e utilizzare i campi indice per l'ordinamento naturale. In tutti gli altri casi utilizzare il comparatore.


-2

La mia libreria di annotazioni per l'implementazione Comparablee 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);         
    }  
}

Fare clic sul collegamento per vedere altri esempi. http://code.google.com/p/compamatic/wiki/CompamaticByExamples

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.