JavaRush /Java Blog /Random-IT /Comparatore in Java

Comparatore in Java

Pubblicato nel gruppo Random-IT
Ciao! Oggi parleremo del confronto degli oggetti. Hmm... Ma sembra che ne abbiamo già parlato più di una volta? :/ Sappiamo come ==funziona l'operatore “ ”, così come i metodi equals()e hashCode(). Il confronto non riguarda proprio questo. In precedenza, intendevamo più come “testare l’uguaglianza degli oggetti”. Comparatore in Java - 1Ma confrontare gli oggetti tra loro può avere obiettivi completamente diversi! Il più ovvio è l'ordinamento. Penso che se ti viene detto di ordinare un elenco di ArrayList<>numeri o stringhe, puoi 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 = "Даша";

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

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

[Даша, Маша, Саша]
È fantastico se ricordi la classe Collectionse il suo metodo sort(). Non credo che ci saranno problemi nemmeno con i numeri. Ecco un compito più difficile 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;
   }

   //...геттеры, сеттеры, 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);
   }
}
È molto semplice: una classe Care 3 dei suoi oggetti. Sii così gentile da ordinare le auto nell'elenco! Probabilmente ti chiederai: “Come dovrebbero essere ordinati?” Per nome, per anno di produzione, per velocità massima? Ottima domanda. Non sappiamo al momento come ordinare gli oggetti della classe Car. E, naturalmente, anche Java non lo sa! Quando proviamo a passare Collections.sort()un elenco di oggetti a un metodo Car, riceveremo 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);

       //ошибка компилятора!
       Collections.sort(cars);
   }
}
E davvero, come fa il linguaggio a sapere esattamente come ordinare gli oggetti che scrivi? Dipende dagli obiettivi del tuo programma. Dobbiamo in qualche modo insegnare a Java a confrontare questi oggetti. E confronta il modo in cui ne abbiamo bisogno. A questo scopo Java dispone di uno speciale strumento di interfaccia Comparable. In inglese questo si traduce con “comparabile”. Affinché i nostri oggetti Carpossano essere confrontati tra loro e in qualche modo ordinati, la classe deve implementare questa interfaccia e implementare il suo 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;
   }

   //...геттеры, сеттеры, toString()

}
Fai attenzione:abbiamo specificato l'interfaccia Comparable<Car>, non solo Comparable. Questa è un'interfaccia tipizzata, ovvero richiede di specificare la classe specifica a cui è associata. In linea di principio <Car>è possibile rimuoverlo dall'interfaccia, ma poi confronta gli oggetti per impostazione predefinita Object. Invece di un metodo compareTo(Car o)nella nostra classe avremo:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Naturalmente per noi è molto più semplice lavorare con i file Car. All'interno del metodo compareTo()implementiamo la logica per il confronto delle macchine. Diciamo che dobbiamo ordinarli per anno di produzione. Probabilmente hai notato che il metodo compareTo()restituisce il valore int, non boolean. Non lasciare che questo ti sorprenda. Il fatto è che confrontare due oggetti ci offre 3 possibili opzioni:
  • а < b
  • a > b
  • a == b.
Ha booleansolo 2 valori: vero e falso, il che è scomodo per confrontare gli oggetti. Tutto è intmolto più semplice. Se il valore restituito > 0è , allora a > b. Se il risultato compareTo < 0è , allora а < b. Ebbene, se il risultato è == 0, allora i due oggetti sono uguali: a == b. Insegnare alla nostra classe a ordinare le auto per anno di produzione è facile come sgusciare le pere:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Cosa sta succedendo qui? Prendiamo un oggetto automobile ( this), l'anno di produzione di questa automobile, e sottraiamo da esso l'anno di fabbricazione di un'altra automobile (quella con cui confrontiamo l'oggetto). Se l'anno di produzione della prima vettura è maggiore, il metodo restituirà int > 0. Ciò significa che un'auto this >è un'auto о. Se invece l'anno di produzione della seconda vettura ( о) è maggiore, allora il metodo restituirà un numero negativo, e quindi о > this. Bene, se sono uguali, il metodo restituirà 0. Un meccanismo così semplice è già sufficiente per ordinare raccolte di oggetti Car! Non è necessario fare nient'altro. Ecco:
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);

       //тут раньше была ошибка
       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 dovrebbero essere! :) Comparatore in Java - 2In quali casi dovrebbe essere utilizzato Comparable? Il metodo di confronto implementato Comparableè chiamato “ordinamento naturale”. Questo perché nel metodo compareTo()descrivi il metodo di confronto più comune che verrà utilizzato per gli oggetti di questa classe nel tuo programma. L'ordinamento naturale è già presente in Java. Ad esempio, Java sa che le stringhe sono spesso ordinate in ordine alfabetico e che i numeri sono spesso ordinati in base al valore crescente. Pertanto, se chiami il metodo su un elenco di numeri o stringhe sort(), questi verranno ordinati. Se nel nostro programma le auto verranno nella maggior parte dei casi confrontate e ordinate per anno di produzione, allora vale la pena definire per loro un ordinamento naturale utilizzando l'interfaccia Comparable<Car>e il metodo compareTo(). Ma cosa succede se questo non ci basta? Immaginiamo che il nostro programma non sia così semplice. Nella maggior parte dei casi, l'ordinamento naturale delle auto (lo impostiamo per anno di produzione) è adatto a noi. Ma a volte tra i nostri clienti ci sono gli appassionati di guida veloce. Se stiamo preparando un catalogo di auto tra cui scegliere, è necessario ordinarle in base alla velocità massima. Comparatore in Java - 3Ad esempio, abbiamo bisogno di tale ordinamento nel 15% dei casi. Ciò chiaramente non è sufficiente per impostare l'ordinamento naturale in base Caralla velocità anziché all'anno di produzione. Ma non possiamo ignorare il 15% dei clienti. Cosa facciamo? Qui un'altra interfaccia viene in nostro aiuto: Comparator. Proprio come , Comparableè digitato. Qual è la differenza? Comparablerende i nostri oggetti "comparabili" e crea per essi l'ordinamento più naturale che verrà utilizzato nella maggior parte dei casi. Comparator- questa è una classe "comparatore" separata (la traduzione è un po' goffa, ma comprensibile). Se dobbiamo implementare un ordinamento specifico, non dobbiamo entrare nella classe Care cambiare la logica compareTo(). Possiamo invece creare una classe comparatrice separata nel nostro programma e insegnarle a 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è abbastanza semplice. Esiste un solo metodo compare(): si tratta di un metodo di interfaccia Comparatorche deve essere implementato. Prende due oggetti come input Care confronta la loro velocità massima nel solito modo (per sottrazione). Ad esempio compareTo(), restituisce il numero int, il principio di confronto è lo stesso. Come possiamo usarlo? Molto 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 metodo Collections.sort()insieme alla lista da ordinare. Avendo ricevuto un comparatore in input, il metodo sort()non utilizzerà l'ordinamento naturale definito nel metodo compareTo()della classe Car. Applicherà invece l'algoritmo di ordinamento del comparatore che gli è stato passato. Quali vantaggi ci dà questo? Innanzitutto, la compatibilità con il codice scritto. Abbiamo creato un nuovo metodo di ordinamento specifico, pur mantenendo quello attuale, che verrà utilizzato nella maggior parte dei casi. Non abbiamo toccato affatto la classe Car. E’ Comparablerimasto com’era:
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();
   }

   //...геттеры, сеттеры, toString()

}
In secondo luogo, la flessibilità. Possiamo aggiungere tutti i tipi che vogliamo. Ad esempio, ordina le auto per colore, velocità, peso o quante volte l'auto è stata utilizzata nei film di Batman. È sufficiente crearne uno aggiuntivo Comparator. È tutto! Oggi hai imparato due meccanismi molto importanti che utilizzerai spesso in progetti reali sul lavoro. Ma, come sai, la teoria senza la pratica non è nulla. Pertanto, è tempo di consolidare le tue conoscenze e risolvere diversi problemi! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION