Perché una classe Java dovrebbe implementare comparabile?


Risposte:


212

Ecco un esempio di vita reale. Si noti che Stringimplementa anche Comparable.

class Author implements Comparable<Author>{
    String firstName;
    String lastName;

    @Override
    public int compareTo(Author other){
        // compareTo should return < 0 if this is supposed to be
        // less than other, > 0 if this is supposed to be greater than 
        // other and 0 if they are supposed to be equal
        int last = this.lastName.compareTo(other.lastName);
        return last == 0 ? this.firstName.compareTo(other.firstName) : last;
    }
}

dopo..

/**
 * List the authors. Sort them by name so it will look good.
 */
public List<Author> listAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    Collections.sort(authors);
    return authors;
}

/**
 * List unique authors. Sort them by name so it will look good.
 */
public SortedSet<Author> listUniqueAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    return new TreeSet<Author>(authors);
}

15
Voglio solo notare che spesso vorrai sovrascrivere equals(e quindi hashCode) per essere coerente con il tuo compareTometodo. Ad esempio, questo è necessario se vuoi che la classe giochi bene con a TreeSet.
Cartuccia

Perché non semplicemente tornare last?
Anirban Nag 'tintinmj',

@ AnirbanNag'tintinmj 'per ordinare automaticamente in base al nome nel caso in cui il cognome sia lo stesso.
OddDev,

Più uno per la buona spiegazione del perché compareTo restituisce un int e cosa significa. Molto utile.
james.garriss,

1
@ user3932000: Esatto, è praticamente il punto di tutte le interfacce. Ma nota che "altri metodi Java" include metodi scritti dall'utente! In realtà direi che la maggior parte delle interfacce sono consumate dal codice dell'utente. In basi di codice più grandi, "te stesso" diventa rapidamente "altri"
Enno Shioji

40

Comparable definisce un ordinamento naturale. Ciò significa che lo stai definendo quando un oggetto dovrebbe essere considerato "minore di" o "maggiore di".

Supponiamo di avere un sacco di numeri interi e di volerli ordinare. È abbastanza facile, basta metterli in una raccolta differenziata, giusto?

TreeSet<Integer> m = new TreeSet<Integer>(); 
m.add(1);
m.add(3);
m.add(2);
for (Integer i : m)
... // values will be sorted

Ma ora supponiamo di avere un oggetto personalizzato, in cui l'ordinamento ha senso per me, ma non è definito. Diciamo che ho dati che rappresentano i distretti per codice postale con densità di popolazione e voglio ordinarli per densità:

public class District {
  String zipcode; 
  Double populationDensity;
}

Ora il modo più semplice per ordinarli è definirli con un ordinamento naturale implementando Comparable, il che significa che esiste un modo standard per definire questi oggetti da ordinare .:

public class District implements Comparable<District>{
  String zipcode; 
  Double populationDensity;
  public int compareTo(District other)
  {
    return populationDensity.compareTo(other.populationDensity);
  }
}

Nota che puoi fare la stessa cosa definendo un comparatore. La differenza è che il comparatore definisce la logica di ordinamento al di fuori dell'oggetto . Forse in un processo separato ho bisogno di ordinare gli stessi oggetti tramite codice postale - in tal caso l'ordinamento non è necessariamente una proprietà dell'oggetto o differisce dall'ordinamento naturale degli oggetti. È possibile utilizzare un comparatore esterno per definire un ordine personalizzato su numeri interi, ad esempio ordinandoli in base al loro valore alfabetico.

Fondamentalmente la logica di ordinamento deve esistere da qualche parte. Quello può essere -

  • nell'oggetto stesso, se è naturalmente confrontabile (estende i comparabili -eg interi)

  • fornito in un comparatore esterno, come nell'esempio sopra.


buon esempio, ma deve essere TreeSet<Integer>invece di TreeMap<Integer>, poiché quest'ultimo non esiste, TreeMaps sono sempre <Key,Value>-pairs. A proposito, un ipotetico TreeMap<District, Object>funzionerebbe solo se District implementasse Comparable, giusto?
Sto

14

Citato dal javadoc;

Questa interfaccia impone un ordinamento totale sugli oggetti di ogni classe che la implementa. Questo ordinamento viene definito ordinamento naturale della classe e il metodo compareTo della classe viene definito metodo di confronto naturale.

Gli elenchi (e le matrici) di oggetti che implementano questa interfaccia possono essere ordinati automaticamente da Collections.sort (e Arrays.sort). Gli oggetti che implementano questa interfaccia possono essere usati come chiavi in ​​una mappa ordinata o come elementi in un insieme ordinato, senza la necessità di specificare un comparatore.

Modifica: ..e ha reso il bit importante in grassetto.


4
Direi che la frase dopo quella che hai evidenziato in grassetto è altrettanto (se non di più) importante.
Michael Borgwardt,

8

Il fatto che una classe implementi Comparablesignifica che puoi prendere due oggetti da quella classe e confrontarli. Alcune classi, come alcune raccolte (funzione di ordinamento in una raccolta) che mantengono gli oggetti per fare affidamento sul fatto che siano comparabili (per ordinare, è necessario sapere quale oggetto è il "più grande" e così via).


8

La maggior parte degli esempi sopra mostra come riutilizzare un oggetto comparabile esistente nella funzione compareTo. Se si desidera implementare il proprio confronto Per quando si desidera confrontare due oggetti della stessa classe, dire un oggetto AirlineTicket che si desidera ordinare in base al prezzo (meno viene classificato per primo), seguito dal numero di scali (di nuovo, meno è al primo posto), faresti quanto segue:

class AirlineTicket implements Comparable<Cost>
{
    public double cost;
    public int stopovers;
    public AirlineTicket(double cost, int stopovers)
    {
        this.cost = cost; this.stopovers = stopovers ;
    }

    public int compareTo(Cost o)
    {
        if(this.cost != o.cost)
          return Double.compare(this.cost, o.cost); //sorting in ascending order. 
        if(this.stopovers != o.stopovers)
          return this.stopovers - o.stopovers; //again, ascending but swap the two if you want descending
        return 0;            
    }
}

6

Un modo semplice per implementare confronti su più campi è con ComparisonChain di Guava - quindi puoi dire

   public int compareTo(Foo that) {
     return ComparisonChain.start()
         .compare(lastName, that.lastName)
         .compare(firstName, that.firstName)
         .compare(zipCode, that.zipCode)
         .result();
   }

invece di

  public int compareTo(Person other) {
    int cmp = lastName.compareTo(other.lastName);
    if (cmp != 0) {
      return cmp;
    }
    cmp = firstName.compareTo(other.firstName);
    if (cmp != 0) {
      return cmp;
    }
    return Integer.compare(zipCode, other.zipCode);
  }
}

3

Ad esempio, quando si desidera avere una raccolta o una mappa ordinata


Ciò che Fernando intende è: se memorizzi "cose" che implementano Comparable in una classe container ordinata, la classe container ordinata può ordinare automaticamente quelle "cose".
Ian Durkan,

2

Comparable viene utilizzato per confrontare le istanze della tua classe. Possiamo confrontare le istanze in molti modi, per questo motivo dobbiamo implementare un metodo compareToper sapere come (attributi) vogliamo confrontare le istanze.

Dog classe:

package test;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    }
}

Main classe:

package test;

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    }
}

Ecco un buon esempio su come utilizzare comparabili in Java:

http://www.onjava.com/pub/a/onjava/2003/03/12/java_comp.html?page=2


2

Quando si implementa l' Comparableinterfaccia, è necessario implementare il metodo compareTo(). È necessario per confrontare gli oggetti, al fine di utilizzare, ad esempio, il metodo di ordinamento della ArrayListclasse. Hai bisogno di un modo per confrontare i tuoi oggetti per poterli ordinare. Quindi hai bisogno di un compareTo()metodo personalizzato nella tua classe in modo da poterlo utilizzare con il ArrayListmetodo di ordinamento. Il compareTo()metodo restituisce -1,0,1.

Ho appena letto un capitolo corrispondente in Java Head 2.0, sto ancora imparando.


1

OK, ma perché non definire un compareTo()metodo senza implementare un'interfaccia comparabile. Ad esempio una classe Citydefinita dal suo namee temperaturee

public int compareTo(City theOther)
{
    if (this.temperature < theOther.temperature)
        return -1;
    else if (this.temperature > theOther.temperature)
        return 1;
    else
        return 0;
}

Questo non funziona. Senza implementare comparabili - ottengo un'eccezione di classe
Karan Ahuja
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.