CodeGym /Java blog /Tilfældig /Javas Comparator-klasse
John Squirrels
Niveau
San Francisco

Javas Comparator-klasse

Udgivet i gruppen
Hej! I dag skal vi tale om at sammenligne objekter. Javas Comparator klasse - 1 Hmm... Men har vi ikke allerede talt om dette emne mere end én gang? :/ Vi ved, hvordan ==operatøren fungerer, samt metoder equals()og hashCode(). Sammenligning er lidt anderledes. Tidligere mente vi højst sandsynligt "kontrol af objekter for lighed". Men årsagerne til at sammenligne objekter med hinanden kan være helt forskellige! Den mest oplagte af disse er sortering. Jeg tror, ​​at hvis du blev bedt om at sortere en ArrayList<>række tal eller strenge, ville du være i stand til at håndtere dette uden 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);
   }
}
Konsoludgang:

[Dasha, Masha, Sasha]
Hvis du huskede Collectionsklassen og dens sort()metode, godt gået! Jeg tror heller ikke du vil have nogen problemer med tal. Her er en mere udfordrende opgave for dig:

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);
   }
}
Opgaven er faktisk enkel. Vi har en Carklasse og 3 bilobjekter. Vil du være venlig at sortere bilerne på listen? Du vil sikkert spørge: "Hvordan skal de sorteres?" Ved navn? Efter fremstillingsår? Med maksimal hastighed? Fremragende spørgsmål. I øjeblikket ved vi ikke, hvordan vi skal sortere objekterne Car. Og ganske naturligt ved Java det heller ikke! Når vi forsøger at sende en liste over Carobjekter til Collections.sort()metoden, får vi en fejl:

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 sproget vide, hvordan man sorterer genstande fra klasser, du har skrevet? Dette afhænger af, hvad dit program skal gøre. Vi skal på en eller anden måde lære Java at sammenligne disse objekter. Og at sammenligne dem, lige som vi vil. Java har en særlig mekanisme til dette: grænsefladen Comparable. For på en eller anden måde at sammenligne og sortere vores Carobjekter, skal klassen implementere denne grænseflade, som består af 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()

}
Bemærk venligstat vi specificerede grænsefladen Comparable<Car>, ikke kun Comparable. Dette er en parameteriseret grænseflade, det vil sige, vi skal specificere den specifikke tilknyttede klasse. I princippet kan du fjerne <Car>fra grænsefladen, men så vil sammenligningen Objectsom standard være baseret på objekter. I stedet for compareTo(Car o)metoden vil vores klasse have:

@Override
   public int compareTo(Object o) {
       return 0;
   }
Det er selvfølgelig meget nemmere for os at arbejde med Car. Inde i compareTo()metoden implementerer vi vores logik til at sammenligne biler. Antag, at vi skal sortere dem efter fremstillingsår. Du har sikkert bemærket, at compareTo()metoden returnerer et int, ikke et boolean. Lad ikke dette overraske dig. Når vi sammenligner to objekter, er der 3 muligheder:
  • а < b
  • a > b
  • a == b.
booleanhar kun 2 værdier: sand og falsk, hvilket ikke fungerer godt til at sammenligne objekter. Med int, er alt meget enklere. Hvis returværdien er > 0, så a > b. Hvis resultatet af compareToer < 0, så a < b. Og hvis resultatet er == 0, så er to objekter ens: a == b. At lære vores klasse at sortere biler efter fremstillingsår er nemt:

@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Men hvad sker der her? Vi tager et bilobjekt ( this), får denne bils fremstillingsår og trækker fremstillingsåret for en anden bil fra det (den, som objektet sammenlignes med). Hvis den første bils fremstillingsår er større, vil metoden returnere en int > 0. Det betyder, at this car >bilen o. Omvendt, hvis fremstillingsåret for den anden bil ( о) er større, vil metoden returnere et negativt tal, hvilket betyder, at o > this. Endelig, hvis de er ens, vil metoden returnere 0. Denne enkle mekanisme er allerede nok til, at vi kan sortere samlinger af Carobjekter! Du behøver ikke gøre andet. Tjek det ud:

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

[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}]
Bilerne er sorteret som vi vil! :) Javas Comparator klasse - 2Hvornår skal jeg bruge Comparable? Sammenligningsmetoden implementeret i Comparablekaldes naturlig bestilling. Dette skyldes, at compareTo()du i metoden definerer den mest almindelige eller naturlige måde at sammenligne objekter i denne klasse på. Java har allerede en naturlig rækkefølge. For eksempel ved Java, at strenge oftest sorteres alfabetisk, og tal ved at øge numerisk værdi. Derfor, hvis du kalder sort()metoden på en liste over tal eller strenge, vil de blive sorteret. Hvis vores program normalt sammenligner og sorterer biler efter fremstillingsår, bør vi definere den naturlige sortering for biler ved hjælp af grænsefladen Comparable<Car>ogcompareTo()metode. Men hvad hvis dette ikke er nok for os? Lad os forestille os, at vores program ikke er så enkelt. I de fleste tilfælde passer den naturlige sortering af biler (som vi har sat til at blive udført efter fremstillingsår) os. Men nogle gange er vores kunder tilhængere af hurtig kørsel. Hvis vi udarbejder et bilkatalog, som de kan gennemse, skal bilerne sorteres efter maksimal hastighed. Javas Comparator-klasse - 3Antag for eksempel, at vi skal sortere sådan 15 % af tiden. Dette er tydeligvis ikke nok til, at vi kan indstille Carklassens naturlige sortering til at være efter hastighed i stedet for efter fremstillingsår. Men vi kan ikke ignorere 15 % af vores kunder. Så hvad gør vi? En anden grænseflade kommer os til hjælp her: Comparator. Ligesom Comparable, det er en parameteriseret grænseflade. Hvad er forskellen? Comparablegør vores objekter "sammenlignelige" og definerer deres mest naturlige sorteringsrækkefølge, altså den sorteringsrækkefølge, der vil blive brugt i de fleste tilfælde. Comparatorer en separat "sammenlignende" grænseflade. Hvis vi skal implementere en form for speciel sorteringsrækkefølge, behøver vi ikke gå ind i Carklassen og ændre logikken i compareTo(). I stedet kan vi oprette en separat klasse, der implementerer Comparator og lære den at udføre den sortering, vi har brug for!

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 vores ret simpelt. Vi skal kun implementere én grænseflademetode: compare(). Den tager to Carobjekter som input og sammenligner deres maksimale hastigheder på den sædvanlige måde (ved subtraktion). Ligesom compareTo(), det returnerer et int, og sammenligningsprincippet er det samme. Hvordan bruger vi dette? Det hele er ligetil:

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

[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 opretter simpelthen et komparatorobjekt og sender det til Collections.sort()metoden sammen med listen, der skal sorteres. Når sort()metoden modtager en komparator, bruger den ikke den naturlige sortering defineret i Carklassens compareTo()metode. I stedet anvender den den sorteringsalgoritme, der er defineret af komparatoren, der er sendt til den. Hvad er fordelene ved at gøre dette? For det første kompatibilitet med eksisterende kode. Vi skabte en ny, speciel sorteringsmetode, samtidig med at vi bibeholdt den eksisterende, som vil blive brugt det meste af tiden. CarVi rørte slet ikke klassen. Det var en Comparable, og sådan forbliver 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 andet fleksibilitet. Vi kan tilføje så mange sorteringsalgoritmer, som vi vil. For eksempel kan vi sortere biler efter farve, hastighed, vægt eller efter hvor mange gange en bil er blevet brugt i Batman-film. Alt, hvad vi skal gøre, er at oprette en ekstra Comparator. Det er det! I dag har du studeret to meget vigtige mekanismer, som du ofte vil bruge i rigtige projekter på arbejdet. Men teori uden praksis er som bekendt ingenting. Nu er det tid til at konsolidere din viden og fuldføre nogle opgaver!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION