CodeGym /Java Blog /Random-IT /Classe Comparator di Java
John Squirrels
Livello 41
San Francisco

Classe Comparator di Java

Pubblicato nel gruppo Random-IT
CIAO! Oggi parleremo di confrontare oggetti. Classe comparatore di Java - 1 Hmm... Ma non abbiamo già parlato di questo argomento più di una volta? :/ Sappiamo come ==funziona l'operatore, così come i metodi equals()e hashCode(). Il confronto è un po' diverso. In precedenza, molto probabilmente intendevamo "controllare l'uguaglianza degli oggetti". Ma le ragioni per confrontare gli oggetti tra loro possono essere completamente diverse! Il più ovvio di questi è l'ordinamento. Penso che se ti venisse detto di ordinare un ArrayList<>numero o una stringa, saresti in grado di gestirlo senza problemi:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       String name1 = "Masha";
       String name2 = "Sasha";
       String name3 = "Dasha";

       List<String> names = new ArrayList<>();
       names.add(name1);
       names.add(name2);
       names.add(name3);

       Collections.sort(names);
       System.out.println(names);
   }
}
Uscita console:

[Dasha, Masha, Sasha]
Se ti sei ricordato della Collectionslezione e del suo sort()metodo, ben fatto! Penso che anche tu non avrai problemi con i numeri. Ecco un compito più impegnativo per te:

public class Car {
  
   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }
  
   // ...getters, setters, toString()
  
}

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);
      
       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);
   }
}
Il compito è in realtà semplice. Abbiamo una Carclasse e 3 oggetti Car. Potrebbe gentilmente ordinare le auto nell'elenco? Probabilmente chiederai: "Come dovrebbero essere ordinati?" Per nome? Per anno di produzione? Per velocità massima? Ottima domanda. Al momento non sappiamo come ordinare gli Caroggetti. E, naturalmente, neanche Java lo sa! Quando proviamo a passare un elenco di Caroggetti al Collections.sort()metodo, otteniamo un errore:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       // Compilation error!
       Collections.sort(cars);
   }
}
E in effetti, come farebbe la lingua a sapere come ordinare gli oggetti delle classi che hai scritto? Questo dipende da cosa deve fare il tuo programma. Dobbiamo in qualche modo insegnare a Java a confrontare questi oggetti. E per confrontarli proprio come vogliamo. Java ha un meccanismo speciale per questo: l' Comparableinterfaccia. Per confrontare e ordinare in qualche modo i nostri Caroggetti, la classe deve implementare questa interfaccia, che consiste in un unico metodo: compareTo():

public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return 0;
   }

   // ...getters, setters, toString()

}
notare cheche abbiamo specificato l' Comparable<Car>interfaccia, non solo Comparable. Questa è un'interfaccia parametrizzata, cioè dobbiamo specificare la specifica classe associata. In linea di principio, puoi rimuovere <Car>dall'interfaccia, ma il confronto sarà basato sugli Objectoggetti per impostazione predefinita. Invece del compareTo(Car o)metodo, la nostra classe avrà:

@Override
   public int compareTo(Object o) {
       return 0;
   }
Certo, è molto più facile per noi lavorare con Car. All'interno del compareTo()metodo, implementiamo la nostra logica per confrontare le auto. Supponiamo di doverli ordinare per anno di produzione. Probabilmente hai notato che il compareTo()metodo restituisce un int, non un boolean. Non lasciare che questo ti sorprenda. Quando confrontiamo due oggetti, ci sono 3 possibilità:
  • а < b
  • a > b
  • a == b.
booleanha solo 2 valori: vero e falso, che non funziona bene per confrontare oggetti. Con int, tutto è molto più semplice. Se il valore restituito è > 0, allora a > b. Se il risultato di compareToè < 0, allora a < b. E, se il risultato è == 0, allora due oggetti sono uguali: a == b. Insegnare alla nostra classe a ordinare le auto per anno di produzione è facile:

@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Ma cosa sta succedendo qui? Prendiamo un oggetto Auto ( this), otteniamo l'anno di produzione di questa auto e sottraiamo da esso l'anno di produzione di un'altra auto (quella con cui l'oggetto viene confrontato). Se l'anno di produzione della prima auto è maggiore, il metodo restituirà un valore int > 0. Ciò significa che l' this car >auto o. Al contrario, se l'anno di produzione della seconda auto ( о) è maggiore, il metodo restituirà un numero negativo, il che significa che o > this. Infine, se sono uguali, il metodo restituirà 0. Questo semplice meccanismo ci basta già per ordinare collezioni di Caroggetti! Non devi fare nient'altro. Controlla:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       // There was previously an error here
       Collections.sort(cars);
       System.out.println(cars);
   }
}
Uscita console:

[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}, 
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
Le auto sono ordinate come vogliamo! :) Classe comparatore di Java - 2Quando dovrei usare Comparable? Il metodo di confronto implementato in Comparableè chiamato ordinamento naturale. Questo perché nel compareTo()metodo definisci il modo più comune, o naturale, di confrontare oggetti di questa classe. Java ha già un ordinamento naturale. Ad esempio, Java sa che le stringhe sono spesso ordinate alfabeticamente e i numeri in base al valore numerico crescente. Pertanto, se chiami il sort()metodo su un elenco di numeri o stringhe, verranno ordinati. Se il nostro programma di solito confronta e ordina le auto per anno di produzione, allora dovremmo definire l'ordinamento naturale per Auto utilizzando l' Comparable<Car>interfaccia e ilcompareTo()metodo. Ma cosa succede se questo non è abbastanza per noi? Immaginiamo che il nostro programma non sia così semplice. Nella maggior parte dei casi, lo smistamento naturale delle auto (che abbiamo impostato per essere eseguito per anno di produzione) ci si addice. Ma a volte i nostri clienti sono appassionati di guida veloce. Se stiamo preparando un catalogo di auto da esaminare, le auto dovrebbero essere ordinate per velocità massima. Classe comparatore di Java - 3Ad esempio, supponiamo di dover ordinare in questo modo il 15% delle volte. Questo chiaramente non è sufficiente per impostare l' Carordinamento naturale della classe in base alla velocità invece che all'anno di produzione. Ma non possiamo ignorare il 15% dei nostri clienti. Quindi cosa facciamo? Un'altra interfaccia ci viene in aiuto qui: Comparator. Proprio come Comparable, è un'interfaccia parametrizzata. Qual è la differenza? Comparablerende i nostri oggetti "comparabili" e definisce il loro ordinamento più naturale, cioè l'ordinamento che verrà utilizzato nella maggior parte dei casi. Comparatorè un'interfaccia di "confronto" separata. Se abbiamo bisogno di implementare una sorta di ordinamento speciale, non abbiamo bisogno di entrare nella Carclasse e cambiare la logica di compareTo(). Invece, possiamo creare una classe separata che implementa Comparator e insegnargli come eseguire l'ordinamento di cui abbiamo bisogno!

import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {
  
   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Come puoi vedere, il nostro Comparatorè piuttosto semplice. Abbiamo bisogno di implementare un solo metodo di interfaccia: compare(). Prende due Caroggetti come input e confronta le loro velocità massime nel solito modo (per sottrazione). Like compareTo(), restituisce an inte il principio di confronto è lo stesso. Come lo usiamo? È tutto semplice:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       Comparator speedComparator = new MaxSpeedCarComparator();
       Collections.sort(cars, speedComparator);

       System.out.println(cars);
   }
}
Uscita console:

[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}, 
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
Creiamo semplicemente un oggetto comparatore e lo passiamo al Collections.sort()metodo insieme alla lista da ordinare. Quando il sort()metodo riceve un comparatore, non utilizza l'ordinamento naturale definito nel metodo Cardella classe compareTo(). Invece, applica l'algoritmo di ordinamento definito dal comparatore passatogli. Quali sono i vantaggi di fare questo? Innanzitutto, la compatibilità con il codice esistente. Abbiamo creato un nuovo metodo di ordinamento speciale, pur mantenendo quello esistente che verrà utilizzato la maggior parte del tempo. Non abbiamo toccato Caraffatto la classe. Era un Comparable, e tale rimane:

public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return this.getManufactureYear() - o.getManufactureYear();
   }

   // ...getters, setters, toString()

}
In secondo luogo, flessibilità. Possiamo aggiungere tutti gli algoritmi di ordinamento che vogliamo. Ad esempio, possiamo ordinare le auto per colore, velocità, peso o per quante volte un'auto è stata utilizzata nei film di Batman. Tutto quello che dobbiamo fare è creare un file Comparator. Questo è tutto! Oggi hai studiato due meccanismi molto importanti che utilizzerai spesso in progetti reali sul lavoro. Ma, come sai, la teoria senza la pratica non è niente. Ora è il momento di consolidare le tue conoscenze e completare alcuni compiti!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION