JavaRush /Blog Java /Random-PL /Komparator w Javie

Komparator w Javie

Opublikowano w grupie Random-PL
Cześć! Dzisiaj porozmawiamy o porównywaniu obiektów. Hmm... Ale zdaje się, że rozmawialiśmy już o tym nie raz? :/ Wiemy jak ==działa operator „ ”, a także metody equals()i hashCode(). Porównanie tak naprawdę nie o to chodzi. Wcześniej mieliśmy na myśli raczej „testowanie równości obiektów”. Komparator w Javie - 1Ale porównywanie obiektów ze sobą może mieć zupełnie inne cele! Najbardziej oczywistym jest sortowanie. Myślę, że jeśli zostaniesz poproszony o posortowanie listy ArrayList<>liczb lub ciągów znaków, poradzisz sobie z tym bez problemów:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       String name1 = "Masza";
       String name2 = „Sasza”;
       String name3 = "Даша";

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

       Collections.sort(names);
       System.out.println(names);
   }
}
Wyjście konsoli:

[Даша, Маша, Саша]
Świetnie, jeśli pamiętasz klasę Collectionsi jej metodę sort(). Myślę, że nie będzie też problemów z liczbami. Oto trudniejsze zadanie dla Ciebie:
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);
   }
}
To bardzo proste: klasa Cari 3 jej obiekty. Bądź tak miły i posortuj samochody na liście! Pewnie zapytacie: „Jak je posortować?” Po nazwie, roku produkcji, maksymalnej prędkości? Świetne pytanie. W tej chwili nie wiemy jak sortować obiekty tej klasy Car. I, całkiem naturalnie, Java też tego nie wie! Przy próbie przekazania Collections.sort()listy obiektów do metody Carotrzymamy błąd:
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);
   }
}
I tak naprawdę, skąd język wie dokładnie, jak sortować obiekty, które piszesz? To zależy od celów Twojego programu. Musimy w jakiś sposób nauczyć Javę porównywania tych obiektów. I porównaj sposób, w jaki tego potrzebujemy. W tym celu Java posiada specjalne narzędzie – interfejs Comparable. W języku angielskim jest to tłumaczone jako „porównywalny”. Aby nasze obiekty Carmogły zostać ze sobą porównane i jakoś posortowane, klasa musi zaimplementować ten interfejs i zaimplementować jego jedyną metodę: 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()

}
Zwróć uwagę:określiliśmy interfejs Comparable<Car>, a nie tylko Comparable. Jest to interfejs typowany, co oznacza, że ​​wymaga określenia konkretnej klasy, z którą jest powiązany. W zasadzie <Car>można go usunąć z interfejsu, ale wtedy domyślnie porównuje obiekty Object. Zamiast metody compareTo(Car o)w naszej klasie będziemy mieli:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Oczywiście dużo łatwiej jest nam pracować Car. Wewnątrz metody compareTo()implementujemy logikę porównywania maszyn. Powiedzmy, że musimy je posortować według roku produkcji. Prawdopodobnie zauważyłeś, że metoda compareTo()zwraca wartość int, a nie boolean. Nie pozwól, żeby Cię to zaskoczyło. Faktem jest, że porównanie dwóch obiektów daje nam 3 możliwe opcje:
  • а < b
  • a > b
  • a == b.
Ma booleantylko 2 wartości - true i false, co jest niewygodne przy porównywaniu obiektów. Wszystko jest intznacznie prostsze. Jeśli wartością zwracaną > 0jest , to a > b. Jeśli wynik compareTo < 0jest taki, to а < b. Cóż, jeśli wynikiem jest == 0, to oba obiekty są równe: a == b. Nauczenie naszej klasy sortowania samochodów według roku produkcji jest tak proste, jak obieranie gruszek:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Co tu się dzieje? Bierzemy jeden przedmiot samochodowy ( this), rok produkcji tego samochodu i odejmujemy od niego rok produkcji innego samochodu (tego, z którym porównujemy przedmiot). Jeżeli rok produkcji pierwszego samochodu będzie większy, metoda powróci int > 0. Co oznacza, że ​​samochód this >to samochód о. Jeśli natomiast rok produkcji drugiego samochodu ( о) będzie większy, to metoda zwróci liczbę ujemną, a zatem о > this. Cóż, jeśli są równe, metoda zwróci 0. Taki prosty mechanizm wystarczy już do sortowania kolekcji obiektów Car! Nie musisz robić nic więcej. Tutaj jesteś:
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);
   }
}
Wyjście konsoli:

[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}]
Samochody są posortowane tak, jak powinny! :) Komparator w Javie - 2W jakich przypadkach warto go stosować Comparable? Zastosowana metoda porównania Comparablenazywa się „porządkowaniem naturalnym”. Dzieje się tak dlatego, że w metodzie compareTo()opisujesz najczęstszą metodę porównywania, która będzie używana dla obiektów tej klasy w Twoim programie. Naturalne porządkowanie jest już obecne w Javie. Na przykład Java wie, że ciągi znaków są najczęściej sortowane alfabetycznie, a liczby najczęściej sortowane są rosnąco. Dlatego jeśli wywołasz metodę na liście liczb lub ciągów znaków sort(), zostaną one posortowane. Jeśli w naszym programie samochody będą w większości przypadków porównywane i sortowane według roku produkcji, to warto zdefiniować dla nich naturalny sort za pomocą interfejsu Comparable<Car>i metody compareTo(). A co jeśli to nam nie wystarczy? Wyobraźmy sobie, że nasz program nie jest taki prosty. W większości przypadków odpowiada nam naturalne sortowanie samochodów (ustawiamy je według roku produkcji). Ale czasami wśród naszych klientów są fani szybkiej jazdy. Jeśli przygotowujemy dla nich katalog samochodów do wyboru, należy je zamówić według prędkości maksymalnej. Komparator w Javie - 3Przykładowo takie sortowanie jest nam potrzebne w 15% przypadków. To wyraźnie nie wystarczy, aby ustawić naturalne sortowanie według Carprędkości, a nie roku produkcji. Ale nie możemy ignorować 15% klientów. Co robimy? Tutaj z pomocą przychodzi nam inny interfejs - Comparator. Podobnie jak , Comparablejest wpisane. Co za różnica? Comparablesprawia, że ​​nasze obiekty są „porównywalne” i tworzy dla nich najbardziej naturalny porządek sortowania, który będzie używany w większości przypadków. Comparator- jest to osobna klasa „porównawcza” (tłumaczenie jest trochę niezdarne, ale zrozumiałe). Jeśli musimy zaimplementować jakieś konkretne sortowanie, nie musimy wchodzić do klasy Cari zmieniać logiki compareTo(). Zamiast tego możemy utworzyć w naszym programie oddzielną klasę komparatora i nauczyć ją sortowania, którego potrzebujemy!
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Jak widać, nasz Comparatorjest dość prosty. Jest tylko jedna metoda compare()- jest to metoda interfejsu Comparator, którą należy zaimplementować. Jako dane wejściowe przyjmuje dwa obiekty Cari porównuje ich maksymalną prędkość w zwykły sposób (przez odejmowanie). Podobnie compareTo()zwraca liczbę int, zasada porównania jest taka sama. Jak możemy to wykorzystać? Bardzo prosta:
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);
   }
}
Wyjście konsoli:

[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}]
Po prostu tworzymy obiekt komparatora i przekazujemy go do metody Collections.sort()wraz z listą do posortowania. Po otrzymaniu na wejściu komparatora metoda sort()nie będzie korzystała z sortowania naturalnego zdefiniowanego w metodzie compareTo()klasy Car. Zamiast tego zastosuje algorytm sortowania z przekazanego mu komparatora. Jakie daje nam to korzyści? Po pierwsze, zgodność z napisanym kodem. Stworzyliśmy nową, specyficzną metodę sortowania, zachowując dotychczasową, która będzie stosowana w większości przypadków. W ogóle nie dotknęliśmy zajęć Car. Pozostał Comparabletaki jaki był:
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()

}
Po drugie, elastyczność. Możemy dodać tyle rodzajów, ile chcemy. Powiedzmy, posortuj samochody według koloru, prędkości, wagi lub tego, ile razy samochód był używany w filmach o Batmanie. Wystarczy stworzyć dodatkowy Comparator. To wszystko! Dziś poznałeś dwa bardzo ważne mechanizmy, które często będziesz wykorzystywał w realnych projektach w pracy. Ale jak wiadomo teoria bez praktyki jest niczym. Zatem czas utrwalić swoją wiedzę i rozwiązać kilka problemów! :)
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION