CodeGym/Blog Java/Random-PL/Klasa Comparator w Javie
Autor
Artem Divertitto
Senior Android Developer at United Tech

Klasa Comparator w Javie

Opublikowano w grupie Random-PL
Cześć! Dzisiaj porozmawiamy o porównywaniu obiektów. Klasa Comparator Java - 1 Hmm... Ale czy już nie raz poruszaliśmy ten temat? :/ Znamy sposób ==działania operatora oraz metody equals()i hashCode(). Porównanie jest trochę inne. Wcześniej najprawdopodobniej mieliśmy na myśli „sprawdzanie równości obiektów”. Ale powody porównywania obiektów ze sobą mogą być zupełnie inne! Najbardziej oczywistym z nich jest sortowanie. Myślę, że gdyby kazano ci posortować ArrayList<>liczby lub ciągi znaków, poradziłbyś sobie z tym bez żadnych 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 = "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);
   }
}
Wyjście konsoli:
[Dasha, Masha, Sasha]
Jeśli pamiętasz Collectionsklasę i jej sort()metodę, brawo! Myślę, że nie będziesz miał 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;
   }

   // ...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);
   }
}
Zadanie jest właściwie proste. Mamy Carklasę i 3 obiekty Samochody. Czy mógłbyś uprzejmie posortować samochody na liście? Prawdopodobnie zapytasz: „Jak należy je posortować?” Wg nazwy? Według roku produkcji? Przy maksymalnej prędkości? Doskonałe pytanie. W tej chwili nie wiemy, jak posortować Carobiekty. I, całkiem naturalnie, Java też tego nie wie! Kiedy próbujemy przekazać listę Carobiektów do Collections.sort()metody, otrzymujemy 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);

       // Compilation error!
       Collections.sort(cars);
   }
}
I rzeczywiście, skąd język miałby wiedzieć, jak sortować obiekty klas, które napisałeś? To zależy od tego, co musi zrobić twój program. Musimy jakoś nauczyć Javę porównywać te obiekty. I porównywać je tak, jak chcemy. Java ma do tego specjalny mechanizm: interfejs Comparable. Aby jakoś porównać i posortować nasze Carobiekty, klasa musi zaimplementować ten interfejs, który składa się z jednej metody 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()

}
Proszę zanotowaćże określiliśmy Comparable<Car>interfejs, a nie tylko Comparable. Jest to interfejs sparametryzowany, to znaczy musimy określić konkretną powiązaną klasę. W zasadzie możesz usunąć <Car>z interfejsu, ale wtedy porównanie będzie Objectdomyślnie oparte na obiektach. Zamiast metody compareTo(Car o)nasza klasa będzie miała:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Oczywiście o wiele łatwiej jest nam pracować z Car. Wewnątrz compareTo()metody implementujemy naszą logikę do porównywania samochodów. Załóżmy, że musimy je posortować według roku produkcji. Prawdopodobnie zauważyłeś, że compareTo()metoda zwraca int, a nie boolean. Niech Cię to nie zaskoczy. Kiedy porównujemy dwa obiekty, istnieją 3 możliwości:
  • а < b
  • a > b
  • a == b.
booleanma tylko 2 wartości: true i false, co nie sprawdza się przy porównywaniu obiektów. Z int, wszystko jest znacznie prostsze. Jeśli zwracana wartość to > 0, to a > b. Jeśli wynikiem compareTojest < 0, to a < b. A jeśli wynikiem jest == 0, to dwa obiekty są równe: a == b. Nauczenie naszej klasy sortowania samochodów według roku produkcji jest łatwe:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Ale co tu się dzieje? Bierzemy jeden obiekt Samochód ( this), pobieramy rok produkcji tego samochodu i odejmujemy od niego rok produkcji innego samochodu (tego, z którym obiekt jest porównywany). Jeśli rok produkcji pierwszego samochodu jest większy, metoda zwróci plik int > 0. Oznacza to, że this car >samochód o. I odwrotnie, jeśli rok produkcji drugiego samochodu ( о) jest większy, to metoda zwróci liczbę ujemną, co oznacza, że o > this​​. Wreszcie, jeśli są równe, metoda zwróci 0. Ten prosty mechanizm wystarczy nam już do posortowania kolekcji Carobiektów! Nie musisz robić nic więcej. Sprawdź to:
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);
   }
}
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}]
Auta są sortowane tak jak chcemy! :) Klasa komparatora Java — 2Kiedy powinienem używać Comparable? Metoda porównywania zaimplementowana w programie Comparablenazywana jest porządkowaniem naturalnym. Dzieje się tak, ponieważ w compareTo()metodzie definiujesz najbardziej powszechny lub naturalny sposób porównywania obiektów tej klasy. Java ma już naturalne uporządkowanie. Na przykład Java wie, że łańcuchy są najczęściej sortowane alfabetycznie, a liczby według rosnącej wartości liczbowej. Dlatego jeśli wywołasz sort()metodę na liście liczb lub łańcuchów, zostaną one posortowane. Jeśli nasz program będzie zwykle porównywał i sortował samochody według roku produkcji, powinniśmy zdefiniować naturalne sortowanie dla samochodów za pomocą interfejsu Comparable<Car>icompareTo()metoda. Ale 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 segregowanie aut (które założyliśmy do wykonania według roku produkcji). Ale czasami nasi klienci są miłośnikami szybkiej jazdy. Jeśli przygotowujemy dla nich katalog samochodów do przejrzenia, samochody powinny być posortowane według maksymalnej prędkości. Klasa Comparator w Javie — 3Załóżmy na przykład, że musimy sortować w ten sposób przez 15% czasu. To najwyraźniej nie wystarczy, abyśmy ustalili Carnaturalne sortowanie tej klasy według prędkości, a nie według roku produkcji. Ale nie możemy zignorować 15% naszych klientów. Więc co robimy? Z pomocą przychodzi nam inny interfejs: Comparator. Podobnie jak Comparable, jest to interfejs parametryczny. Co za różnica? Comparablesprawia, że ​​nasze obiekty są "porównywalne" i określa ich najbardziej naturalny porządek sortowania, czyli porządek, który będzie używany w większości przypadków. Comparatorjest oddzielnym interfejsem „porównującym”. Jeśli musimy zaimplementować jakąś specjalną kolejność sortowania, nie musimy wchodzić do Carklasy i zmieniać logiki compareTo(). Zamiast tego możemy stworzyć oddzielną klasę, która implementuje Comparator i nauczyć ją, jak przeprowadzać sortowanie, 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. Musimy zaimplementować tylko jedną metodę interfejsu: compare(). Bierze dwa Carobiekty jako dane wejściowe i porównuje ich maksymalne prędkości w zwykły sposób (przez odejmowanie). Podobnie jak compareTo(), zwraca an int, a zasada porównania jest taka sama. Jak tego używamy? To wszystko jest proste:
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 Collections.sort()metody wraz z listą do posortowania. Kiedy sort()metoda otrzymuje komparator, nie używa sortowania naturalnego zdefiniowanego w metodzie Carklasy compareTo(). Zamiast tego stosuje algorytm sortowania zdefiniowany przez przekazany do niego komparator. Jakie są zalety takiego postępowania? Po pierwsze, kompatybilność z istniejącym kodem. Stworzyliśmy nową, specjalną metodę sortowania, zachowując istniejącą, która będzie używana przez większość czasu. CarW ogóle nie dotknęliśmy klasy. To było Comparablei tak zostało:
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()

}
Po drugie elastyczność. Możemy dodać dowolną liczbę algorytmów sortowania. Na przykład możemy sortować samochody według koloru, prędkości, wagi lub według tego, ile razy samochód był używany w filmach o Batmanie. Wszystko, co musimy zrobić, to utworzyć dodatkowy plik Comparator. Otóż ​​to! Dzisiaj poznałeś dwa bardzo ważne mechanizmy, które będziesz często wykorzystywać w prawdziwych projektach w pracy. Ale jak wiadomo teoria bez praktyki jest niczym. Teraz nadszedł czas, aby utrwalić swoją wiedzę i wykonać kilka zadań!
Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy