CodeGym /Java-blogg /Tilfeldig /Javas Comparator-klasse
John Squirrels
Nivå
San Francisco

Javas Comparator-klasse

Publisert i gruppen
Hei! I dag skal vi snakke om å sammenligne objekter. Javas komparatorklasse - 1 Hmm... Men har vi ikke allerede snakket om dette emnet mer enn én gang? :/ Vi vet hvordan operatøren ==fungerer, samt metodene equals()og hashCode(). Sammenligning er litt annerledes. Tidligere mente vi mest sannsynlig «å sjekke objekter for likestilling». Men årsakene til å sammenligne objekter med hverandre kan være helt forskjellige! Den mest åpenbare av disse er sortering. Jeg tror at hvis du ble bedt om å sortere en ArrayList<>av tall eller strenger, ville du kunne håndtere dette uten problemer:

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);
   }
}
Konsoll utgang:

[Dasha, Masha, Sasha]
Hvis du husket Collectionsklassen og dens sort()metode, godt gjort! Jeg tror du heller ikke vil ha noen problemer med tall. Her er en mer utfordrende oppgave for deg:

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);
   }
}
Oppgaven er egentlig enkel. Vi har en Carklasse og 3 bilobjekter. Vil du være så snill å sortere bilene i listen? Du vil sannsynligvis spørre: "Hvordan skal de sorteres?" Ved navn? Etter produksjonsår? Med maksimal hastighet? Utmerket spørsmål. For øyeblikket vet vi ikke hvordan vi skal sortere objektene Car. Og, ganske naturlig, vet ikke Java det heller! Når vi prøver å sende en liste over Carobjekter til Collections.sort()metoden, får vi en feilmelding:

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);
   }
}
Og faktisk, hvordan ville språket vite hvordan det skal sortere objekter i klasser du har skrevet? Dette avhenger av hva programmet ditt trenger å gjøre. Vi må på en eller annen måte lære Java å sammenligne disse objektene. Og å sammenligne dem akkurat slik vi vil. Java har en spesiell mekanisme for dette: grensesnittet Comparable. For på en eller annen måte å sammenligne og sortere Carobjektene våre, må klassen implementere dette grensesnittet, som består av en enkelt metode: 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()

}
Vær oppmerksom påat vi spesifiserte grensesnittet Comparable<Car>, ikke bare Comparable. Dette er et parameterisert grensesnitt, det vil si at vi må spesifisere den spesifikke tilknyttede klassen. I prinsippet kan du fjerne <Car>fra grensesnittet, men da vil sammenligningen være basert på Objectobjekter som standard. I stedet for compareTo(Car o)metoden vil klassen vår ha:

@Override
   public int compareTo(Object o) {
       return 0;
   }
Selvfølgelig er det mye lettere for oss å jobbe med Car. Inne i compareTo()metoden implementerer vi vår logikk for å sammenligne biler. Anta at vi må sortere dem etter produksjonsår. Du har sikkert lagt merke til at compareTo()metoden returnerer en int, ikke en boolean. Ikke la dette overraske deg. Når vi sammenligner to objekter, er det 3 muligheter:
  • а < b
  • a > b
  • a == b.
booleanhar bare 2 verdier: sann og usann, som ikke fungerer bra for å sammenligne objekter. Med int, er alt mye enklere. Hvis returverdien > 0er a > b. Hvis resultatet av compareToer < 0, da a < b. Og hvis resultatet er == 0, er to objekter like: a == b. Å lære klassen vår å sortere biler etter produksjonsår er enkelt:

@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Men hva skjer her? Vi tar ett bilobjekt ( this), får denne bilens produksjonsår, og trekker fra det produksjonsåret til en annen bil (den som objektet sammenlignes med). Hvis den første bilens produksjonsår er større, vil metoden returnere en int > 0. Dette betyr at this car >bilen o. Omvendt, hvis produksjonsåret for den andre bilen ( о) er større, vil metoden returnere et negativt tall, som betyr at o > this. Til slutt, hvis de er like, vil metoden returnere 0. Denne enkle mekanismen er allerede nok for oss til å sortere samlinger av Cargjenstander! Du trenger ikke gjøre noe annet. Sjekk det ut:

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);
   }
}
Konsoll utgang:

[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}]
Bilene er sortert som vi vil! :) Javas komparatorklasse - 2Når bør jeg bruke Comparable? Sammenligningsmetoden implementert i Comparablekalles naturlig bestilling. Dette er fordi du i compareTo()metoden definerer den vanligste, eller naturlige, måten å sammenligne objekter i denne klassen på. Java har allerede en naturlig rekkefølge. For eksempel vet Java at strenger oftest sorteres alfabetisk, og tall ved å øke numerisk verdi. Derfor, hvis du kaller sort()metoden på en liste med tall eller strenger, vil de bli sortert. Hvis programmet vårt vanligvis sammenligner og sorterer biler etter produksjonsår, bør vi definere den naturlige sorteringen for biler ved å bruke grensesnittet Comparable<Car>ogcompareTo()metode. Men hva om dette ikke er nok for oss? La oss forestille oss at programmet vårt ikke er så enkelt. I de fleste tilfeller passer den naturlige sorteringen av biler (som vi har satt til å utføres etter produksjonsår) oss. Men noen ganger er kundene våre tilhengere av rask kjøring. Hvis vi utarbeider en bilkatalog som de kan lese, bør bilene sorteres etter maksimal hastighet. Javas komparatorklasse - 3Anta for eksempel at vi må sortere slik 15 % av tiden. Dette er tydeligvis ikke nok til at vi kan sette Carklassens naturlige sortering til å være etter hastighet i stedet for etter produksjonsår. Men vi kan ikke ignorere 15 % av kundene våre. Så hva gjør vi? Et annet grensesnitt kommer til hjelp her: Comparator. Akkurat som Comparable, er det et parameterisert grensesnitt. Hva er forskjellen? Comparablegjør objektene våre "sammenlignbare" og definerer deres mest naturlige sorteringsrekkefølge, dvs. sorteringsrekkefølgen som vil bli brukt i de fleste tilfeller. Comparatorer et eget "sammenlignings"-grensesnitt. Hvis vi trenger å implementere en slags spesiell sorteringsrekkefølge, trenger vi ikke gå inn i klassen Carog endre logikken til compareTo(). I stedet kan vi lage en egen klasse som implementerer Comparator og lære den hvordan den skal utføre sorteringen vi trenger!

import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {
  
   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Som du kan se, Comparatorer vår ganske enkel. Vi trenger bare å implementere én grensesnittmetode: compare(). Den tar to Carobjekter som innganger og sammenligner deres maksimale hastigheter på vanlig måte (ved subtraksjon). Som compareTo(), den returnerer en int, og sammenligningsprinsippet er det samme. Hvordan bruker vi dette? Det hele er enkelt:

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);
   }
}
Konsoll utgang:

[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}]
Vi lager ganske enkelt et komparatorobjekt og sender det til Collections.sort()metoden sammen med listen som skal sorteres. Når sort()metoden mottar en komparator, bruker den ikke den naturlige sorteringen som er definert i Carklassens compareTo()metode. I stedet bruker den sorteringsalgoritmen definert av komparatoren som er sendt til den. Hva er fordelene med å gjøre dette? Først kompatibilitet med eksisterende kode. Vi laget en ny, spesiell sorteringsmetode, samtidig som vi beholder den eksisterende som vil bli brukt mesteparten av tiden. Vi rørte ikke Carklassen i det hele tatt. Det var en Comparable, og slik forblir det:

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

}
For det andre, fleksibilitet. Vi kan legge til så mange sorteringsalgoritmer vi vil. For eksempel kan vi sortere biler etter farge, hastighet, vekt eller etter hvor mange ganger en bil har blitt brukt i Batman-filmer. Alt vi trenger å gjøre er å lage en ekstra Comparator. Det er det! I dag har du studert to svært viktige mekanismer som du ofte vil bruke i virkelige prosjekter på jobben. Men som du vet er teori uten praksis ingenting. Nå er det på tide å konsolidere kunnskapen din og fullføre noen oppgaver!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION