Szia! Ma az objektumok összehasonlításáról fogunk beszélni. Hmm... De nem beszéltünk már többször erről a témáról? :/ Ismerjük az
==
operátor működését, valamint a equals()
és hashCode()
módszereket. Az összehasonlítás egy kicsit más. Korábban nagy valószínűséggel az "tárgyak egyenlőségének ellenőrzése" alatt értünk. De az objektumok egymással való összehasonlításának okai teljesen eltérőek lehetnek! Ezek közül a legkézenfekvőbb a válogatás. Azt hiszem, ha azt mondanák, hogy rendezzen ArrayList<>
számokat vagy karakterláncokat, akkor ezt gond nélkül kezelné:
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);
}
}
Konzol kimenet:
[Dasha, Masha, Sasha]
Ha emlékszel az Collections
osztályra és annak sort()
módszerére, akkor jó! Szerintem a számokkal sem lesz gond. Itt van egy nagyobb kihívást jelentő feladat az Ön számára:
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);
}
}
A feladat valójában egyszerű. Van egy Car
osztályunk és 3 autó objektumunk. Leválogatnád a listán szereplő autókat? Valószínűleg azt fogja kérdezni: "Hogyan kell őket rendezni?" Név szerint? Gyártási év szerint? Maximális sebességgel? Kiváló kérdés. Jelenleg nem tudjuk, hogyan kell rendezni a Car
tárgyakat. És természetesen ezt a Java sem tudja! Amikor objektumok listáját próbáljuk átadni Car
a metódusnak Collections.sort()
, hibát kapunk:
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);
}
}
És valóban, honnan tudná a nyelv, hogyan kell rendezni az Ön által írt osztályok objektumait? Ez attól függ, mit kell tennie a programnak. Valahogy meg kell tanítanunk a Java-t ezen objektumok összehasonlítására. És összehasonlítani őket úgy, ahogy szeretnénk. A Java-nak van egy speciális mechanizmusa erre: az Comparable
interfész. Ahhoz, hogy valamilyen módon összehasonlíthassuk és rendezzük az objektumainkat Car
, az osztálynak meg kell valósítania ezt a felületet, amely egyetlen metódusból áll: 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()
}
Kérjük, vegye figyelembehogy megadtuk a Comparable<Car>
felületet, nem csak a Comparable
. Ez egy paraméterezett interfész, vagyis meg kell adnunk az adott társított osztályt. Elvileg lehet eltávolítani <Car>
a felületről, de akkor az összehasonlítás Object
alapértelmezés szerint objektumok alapján történik. A metódus helyett compareTo(Car o)
az osztályunkban a következő lesz:
@Override
public int compareTo(Object o) {
return 0;
}
Természetesen sokkal könnyebben dolgozhatunk vele Car
. A compareTo()
módszeren belül megvalósítjuk az autók összehasonlítására vonatkozó logikánkat. Tegyük fel, hogy gyártási év szerint kell rendeznünk őket. Valószínűleg észrevette, hogy a compareTo()
metódus nem a int
, hanem egy boolean
. Ne hagyd, hogy ez meglepjen. Ha két objektumot összehasonlítunk, 3 lehetőség van:
а < b
a > b
a == b
.
boolean
csak 2 értéke van: igaz és hamis, ami nem működik jól az objektumok összehasonlításakor. A segítségével int
minden sokkal egyszerűbb. Ha a visszatérési érték > 0
, akkor a > b
. Ha az eredménye , compareTo
akkor . És ha az eredmény , akkor két objektum egyenlő: . Könnyű megtanítani osztályunkat az autók gyártási év szerinti osztályozására: < 0
a < b
== 0
a == b
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
De mi folyik itt? Vegyünk egy autó tárgyat ( this
), megkapjuk ennek az autónak a gyártási évét, és levonjuk belőle egy másik autó gyártási évét (amelyhez az objektumot hasonlítják). Ha az első autó gyártási éve nagyobb, a metódus egy int > 0
. Ez azt jelenti, hogy az this car >
autó o
. Ezzel szemben, ha a második autó gyártási éve ( о
) nagyobb, akkor a módszer negatív számot ad vissza, ami azt jelenti, hogy o > this
. Végül, ha egyenlőek, akkor a metódus visszatér 0
. Ez az egyszerű mechanizmus már elegendő ahhoz, hogy tárgygyűjteményeket rendezzünk Car
! Nem kell mást tenned. Nézd meg:
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);
}
}
Konzol kimenet:
[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}]
Az autók úgy vannak szétválogatva, ahogy akarjuk! :) Mikor használjam Comparable
? A ben megvalósított összehasonlítási módszert Comparable
természetes rendezésnek nevezzük. Ennek az az oka, hogy a metódusban compareTo()
meghatározza az osztály objektumai összehasonlításának leggyakoribb vagy természetes módját. A Java-nak már van természetes sorrendje. Például a Java tudja, hogy a karakterláncokat leggyakrabban ábécé szerint rendezik, a számokat pedig a számértékek növelésével. sort()
Ezért, ha a metódust számok vagy karakterláncok listáján hívja meg , akkor azok rendezve lesznek. Ha a programunk általában az autókat gyártási év szerint hasonlítja össze és rendezi, akkor az autók természetes válogatását a Comparable<Car>
felület és acompareTo()
módszer. De mi van, ha ez nekünk nem elég? Képzeljük el, hogy a programunk nem olyan egyszerű. A legtöbb esetben az autók természetes válogatása (amelyet gyártási évenként állítottunk be) megfelel nekünk. Ügyfeleink azonban néha a gyors vezetés szerelmesei. Ha autókatalógust készítünk, hogy átnézzék, az autókat maximális sebesség szerint kell válogatni. Tegyük fel például, hogy az esetek 15%-ában így kell rendeznünk. Car
Ez nyilvánvalóan nem elég ahhoz, hogy az osztály természetes válogatását sebességre állítsuk be a gyártási év helyett. De nem hagyhatjuk figyelmen kívül ügyfeleink 15%-át. Szóval mit csináljunk? Itt egy másik felület is segítségünkre van: Comparator
. Akárcsak a Comparable
, ez is egy paraméterezett interfész. Mi a különbség? Comparable
"összehasonlíthatóvá" teszi tárgyainkat, és meghatározza azok legtermészetesebb rendezési sorrendjét, vagyis azt a rendezési sorrendet, amelyet a legtöbb esetben használni fognak. Comparator
egy különálló "összehasonlító" felület. Ha valamilyen speciális rendezési sorrendet kell megvalósítanunk, akkor nem kell bemennünk az Car
osztályba és megváltoztatni a logikáját compareTo()
. Ehelyett létrehozhatunk egy külön osztályt, amely megvalósítja a Comparator-t, és megtanítjuk neki a szükséges rendezést!
import java.util.Comparator;
public class MaxSpeedCarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getMaxSpeed() - o2.getMaxSpeed();
}
}
Amint látja, a mi feladatunk Comparator
nagyon egyszerű. Csak egy interfész módszert kell megvalósítanunk: compare()
. Car
Bemenetként két objektumot vesz fel , és a szokásos módon (kivonással) összehasonlítja azok maximális sebességét. Hasonlóan compareTo()
az an -t adja vissza int
, és az összehasonlítás elve ugyanaz. Hogyan használjuk ezt? Minden egyértelmű:
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);
}
}
Konzol kimenet:
[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}]
Egyszerűen létrehozunk egy összehasonlító objektumot, és átadjuk a Collections.sort()
metódusnak a rendezendő listával együtt. Amikor a sort()
metódus komparátort kap, nem használja az Car
osztály compareTo()
metódusában meghatározott természetes rendezést. Ehelyett a neki átadott összehasonlító által meghatározott rendezési algoritmust alkalmazza. Milyen előnyei vannak ennek? Először is, kompatibilitás a meglévő kóddal. Létrehoztunk egy új, speciális válogatási módszert, megtartva a meglévőt, amelyet legtöbbször alkalmazni fogunk. Car
Egyáltalán nem nyúltunk az osztályhoz. Ez volt Comparable
, és így marad:
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()
}
Másodszor a rugalmasság. Tetszőleges számú rendezési algoritmust adhatunk hozzá. Például sorba rendezhetjük az autókat szín, sebesség, tömeg vagy aszerint, hogy hányszor használtak egy autót a Batman-filmekben. Nincs más dolgunk, mint létrehozni egy további Comparator
. Ez az! Ma két nagyon fontos mechanizmust tanulmányoztál, amelyeket gyakran fogsz használni valós munkád során. De mint tudod, az elmélet gyakorlat nélkül semmi. Itt az ideje, hogy megszilárdítsa tudását, és elvégezzen néhány feladatot!