CodeGym /Java Blog /Willekeurig /Java's Comparator-klasse
John Squirrels
Niveau 41
San Francisco

Java's Comparator-klasse

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag gaan we het hebben over het vergelijken van objecten. Java's Comparator-klasse - 1 Hmm... Maar hebben we dit onderwerp niet al vaker besproken? :/ We weten hoe de ==operator werkt, evenals de methoden equals()en hashCode(). Vergelijking is een beetje anders. Voorheen bedoelden we hoogstwaarschijnlijk "objecten controleren op gelijkheid". Maar de redenen om objecten met elkaar te vergelijken kunnen totaal verschillend zijn! De meest voor de hand liggende hiervan is sorteren. Ik denk dat als je zou worden verteld om een ​​aantal ArrayList<>getallen of strings te sorteren, je dit zonder problemen zou kunnen verwerken:

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

[Dasha, Masha, Sasha]
CollectionsAls je je de klas en de methode herinnerde sort(), goed gedaan! Ik denk dat je ook geen moeite zult hebben met cijfers. Hier is een meer uitdagende taak voor jou:

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);
   }
}
De taak is eigenlijk eenvoudig. We hebben een Carklasse en 3 auto-objecten. Zou u zo vriendelijk willen zijn de auto's in de lijst te sorteren? U zult waarschijnlijk vragen: "Hoe moeten ze worden gesorteerd?" Bij naam? Op bouwjaar? Op maximale snelheid? Uitstekende vraag. Op dit moment weten we niet hoe we de Carobjecten moeten sorteren. En natuurlijk weet Java dat ook niet! Wanneer we proberen een lijst met Carobjecten door te geven aan de Collections.sort()methode, krijgen we een foutmelding:

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);
   }
}
En inderdaad, hoe zou de taal weten hoe objecten van klassen die je hebt geschreven moeten worden gesorteerd? Dit hangt af van wat uw programma moet doen. We moeten Java op de een of andere manier leren deze objecten te vergelijken. En om ze te vergelijken zoals we willen. Java heeft hiervoor wel een speciaal mechanisme: de Comparableinterface. Om onze Carobjecten op de een of andere manier te kunnen vergelijken en sorteren, moet de klasse deze interface implementeren, die uit één enkele methode bestaat: 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()

}
Houd er rekening mee datdat we de Comparable<Car>interface hebben gespecificeerd, niet alleen Comparable. Dit is een geparametriseerde interface, dat wil zeggen dat we de specifieke bijbehorende klasse moeten specificeren. In principe kun je <Car>uit de interface verwijderen, maar dan zal de vergelijking Objectstandaard op objecten gebaseerd zijn. In plaats van de compareTo(Car o)methode heeft onze klas:

@Override
   public int compareTo(Object o) {
       return 0;
   }
Het is natuurlijk veel gemakkelijker voor ons om mee te werken Car. Binnen de compareTo()methode implementeren we onze logica voor het vergelijken van auto's. Stel dat we ze moeten sorteren op bouwjaar. Het is je waarschijnlijk opgevallen dat de compareTo()methode een retourneert intin plaats van een boolean. Laat je hierdoor niet verrassen. Wanneer we twee objecten vergelijken, zijn er 3 mogelijkheden:
  • а < b
  • a > b
  • a == b.
booleanheeft slechts 2 waarden: waar en onwaar, wat niet goed werkt voor het vergelijken van objecten. Met intis alles veel eenvoudiger. Als de retourwaarde > 0, dan is a > b. Als het resultaat van compareTois < 0, dan a < b. En als het resultaat is == 0, dan zijn twee objecten gelijk: a == b. Onze klas leren auto's te sorteren op bouwjaar is gemakkelijk:

@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Maar wat is hier aan de hand? We nemen één auto-object ( this), halen het bouwjaar van deze auto en trekken het bouwjaar van een andere auto af (degene waarmee het object wordt vergeleken). Als het bouwjaar van de eerste auto groter is, retourneert de methode een int > 0. Dit betekent dat de this car >auto o. Omgekeerd, als het bouwjaar van de tweede auto ( о) groter is, retourneert de methode een negatief getal, wat betekent dat o > this. Als ze uiteindelijk gelijk zijn, retourneert de methode 0. Dit eenvoudige mechanisme is voor ons al voldoende om collecties Carobjecten te sorteren! U hoeft verder niets te doen. Bekijken:

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

[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}]
De auto's zijn gesorteerd zoals we willen! :) Java's Comparator-klasse - 2Wanneer moet ik gebruiken Comparable? De vergelijkingsmethode die erin is geïmplementeerd, Comparablewordt natuurlijke ordening genoemd. Dit komt omdat u in de compareTo()methode de meest gebruikelijke of natuurlijke manier definieert om objecten van deze klasse te vergelijken. Java heeft al een natuurlijke ordening. Java weet bijvoorbeeld dat tekenreeksen meestal alfabetisch worden gesorteerd en getallen op oplopende numerieke waarde. Daarom, als u de methode aanroept sort()op een lijst met getallen of strings, worden ze gesorteerd. Als ons programma gewoonlijk auto's vergelijkt en sorteert op bouwjaar, dan moeten we de natuurlijke sortering voor auto's definiëren met behulp van de Comparable<Car>interface en decompareTo()methode. Maar wat als dit niet genoeg voor ons is? Laten we ons voorstellen dat ons programma niet zo eenvoudig is. In de meeste gevallen past de natuurlijke sortering van auto's (die we hebben ingesteld op bouwjaar) bij ons. Maar soms zijn onze klanten liefhebbers van snel rijden. Als we een autocatalogus aan het voorbereiden zijn die ze kunnen doornemen, moeten de auto's worden gesorteerd op maximale snelheid. Java's Comparator-klasse - 3Stel dat we 15% van de tijd op deze manier moeten sorteren. Dit is duidelijk niet genoeg voor ons om de Carnatuurlijke sortering van de klasse in te stellen op snelheid in plaats van op bouwjaar. Maar we kunnen niet om 15% van onze klanten heen. Dus wat doen we? Een andere interface komt ons hier te hulp: Comparator. Net als Comparable, is het een geparametriseerde interface. Wat is het verschil? Comparablemaakt onze objecten "vergelijkbaar" en definieert hun meest natuurlijke sorteervolgorde, dwz de sorteervolgorde die in de meeste gevallen zal worden gebruikt. Comparatoris een aparte "vergelijkende" interface. Als we een soort speciale sorteervolgorde moeten implementeren, hoeven we niet naar de Carklas te gaan en de logica van te veranderen compareTo(). In plaats daarvan kunnen we een aparte klasse maken die Comparator implementeert en deze leren hoe de sortering moet worden uitgevoerd die we nodig hebben!

import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {
  
   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Zoals je kunt zien, Comparatoris ons vrij eenvoudig. We hoeven slechts één interfacemethode te implementeren: compare(). Het neemt twee Carobjecten als invoer en vergelijkt hun maximale snelheden op de gebruikelijke manier (door aftrekken). Zoals compareTo(), het retourneert de an int, en het vergelijkingsprincipe is hetzelfde. Hoe gebruiken we dit? Het is allemaal eenvoudig:

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

[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}]
We maken eenvoudig een comparatorobject en geven dit door aan de Collections.sort()methode samen met de lijst die moet worden gesorteerd. Wanneer de sort()methode een comparator ontvangt, gebruikt deze niet de natuurlijke sortering die is gedefinieerd in de methode Carvan de klasse compareTo(). In plaats daarvan past het het sorteeralgoritme toe dat is gedefinieerd door de vergelijker die eraan is doorgegeven. Wat zijn de voordelen om dit te doen? Ten eerste, compatibiliteit met bestaande code. We hebben een nieuwe, speciale sorteermethode ontwikkeld, met behoud van de bestaande die het meeste zal worden gebruikt. We hebben de Carklas helemaal niet aangeraakt. Het was een Comparable, en zo blijft het:

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

}
Ten tweede, flexibiliteit. We kunnen zoveel sorteeralgoritmen toevoegen als we willen. We kunnen auto's bijvoorbeeld sorteren op kleur, snelheid, gewicht of op hoe vaak een auto in Batman-films is gebruikt. Het enige wat we hoeven te doen is een extra Comparator. Dat is het! Vandaag heb je twee zeer belangrijke mechanismen bestudeerd die je vaak zult gebruiken in echte projecten op het werk. Maar zoals u weet, is theorie zonder praktijk niets. Nu is het tijd om uw kennis te consolideren en enkele taken uit te voeren!
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION