Usporednik i usporediv u Javi

1. Uvod

Usporedbe u Javi prilično su jednostavne - sve dok nisu.

Kada radimo s prilagođenim vrstama ili pokušavamo usporediti objekte koji nisu izravno usporedivi, trebamo se poslužiti strategijom usporedbe. Možemo ga jednostavno izraditi, ali koristeći ga Usporednik ili Usporedive sučelja.

2. Postavljanje primjera

Uzmimo primjer nogometne momčadi - gdje želimo poredati igrače prema njihovoj ljestvici.

Počet ćemo s izradom jednostavnog Igrač razred:

igrač javne klase {privatni int poredak; privatni naziv niza; privatno int doba; // konstruktor, getteri, postavljači}

Dalje, izradimo a PlayerSorter razreda stvoriti našu kolekciju i pokušati je razvrstati pomoću Zbirke.sort:

public static void main (String [] args) {Popis footballTeam = new ArrayList (); Igrač igrač1 = novi igrač (59, "Ivan", 20); Igrač igrač2 = novi igrač (67, "Roger", 22); Igrač igrač3 = novi igrač (45, "Steven", 24); footballTeam.add (igrač1); footballTeam.add (igrač2); footballTeam.add (player3); System.out.println ("Prije sortiranja:" + footballTeam); Collections.sort (footballTeam); System.out.println ("Nakon sortiranja:" + footballTeam); } 

Ovdje, kao što se očekivalo, ovo rezultira pogreškom u vremenu kompajliranja:

Razvrstavanje metode (Popis) u tipu Zbirke nije primjenjivo za argumente (ArrayList)

Shvatimo što smo ovdje pogriješili.

3. Usporedive

Kao što i samo ime govori, Usporedive je sučelje koje definira strategiju uspoređivanja objekta s drugim objektima iste vrste. To se naziva "prirodnim uređenjem" klase.

Sukladno tome, da bismo mogli sortirati - moramo definirati svoje Igrač objekt kao usporediv primjenom Usporedive sučelje:

javna klasa Player implementira Usporedivo {// isto kao i prije @Override public int compareTo (Player otherPlayer) {return Integer.compare (getRanking (), otherPlayer.getRanking ()); }} 

Redoslijed sortiranja određuje povratna vrijednost compareTo ()metoda. The Integer.compored (x, y) vraća -1 ako x je manje od g, vraća 0 ako su jednaki, a u suprotnom vraća 1.

Metoda vraća broj koji pokazuje je li objekt koji se uspoređuje manji od, jednak ili veći od objekta koji se prosljeđuje kao argument.

Napokon, kad pokrenemo svoj PlayerSorter sada, možemo vidjeti svoje Igrači poredano prema njihovom rangu:

Prije sortiranja: [John, Roger, Steven] Nakon sortiranja: [Steven, John, Roger]

Sada kada imamo jasno razumijevanje prirodnog uređenja s Usporedive, da vidimo kako možemo koristiti druge vrste naručivanja, na fleksibilniji način nego izravno implementiranje sučelja.

4. Usporednik

The Usporednik sučelje definira a usporedi (arg1, arg2) metoda s dva argumenta koji predstavljaju uspoređene objekte i radi slično kao Comparable.compareTo () metoda.

4.1. Stvaranje Usporednici

Za stvaranje a Usporednik, moramo provesti Usporednik sučelje.

U našem prvom primjeru stvorit ćemo a Usporednik koristiti rangiranje atribut Igrač za razvrstavanje igrača:

javna klasa PlayerRankingComparator implementira Comparator {@Override public int compare (Player firstPlayer, Player secondPlayer) {return Integer.compare (firstPlayer.getRanking (), secondPlayer.getRanking ()); }}

Slično tome, možemo stvoriti Usporednik koristiti dob atribut Igrač za razvrstavanje igrača:

javna klasa PlayerAgeComparator implementira Comparator {@Override public int compare (Player firstPlayer, Player secondPlayer) {return Integer.compare (firstPlayer.getAge (), secondPlayer.getAge ()); }}

4.2. Usporednici na djelu

Da bismo demonstrirali koncept, izmijenimo naš PlayerSorter uvođenjem drugog argumenta u Zbirke.sort metoda što je zapravo instanca Usporednik želimo koristiti.

Korištenjem ovog pristupa možemo nadjačati prirodni poredak:

PlayerRankingComparator playerComparator = novi PlayerRankingComparator (); Collections.sort (footballTeam, playerComparator); 

A sad, pokrenimo naš PlayerRankingSorter na pogledajte rezultat:

Prije sortiranja: [John, Roger, Steven] Nakon sortiranja po rangiranju: [Steven, John, Roger]

Ako želimo drugačiji redoslijed sortiranja, trebamo samo promijeniti Usporednik koristimo:

PlayerAgeComparator playerComparator = novi PlayerAgeComparator (); Collections.sort (footballTeam, playerComparator);

Sad, kad pokrenemo svoj PlayerAgeSorter, možemo vidjeti drugačiji redoslijed sortiranja po dob:

Prije sortiranja: [John, Roger, Steven] Nakon sortiranja po dobi: [Roger, John, Steven]

4.3. Java 8 Usporednici

Java 8 pruža nove načine definiranja Usporednici pomoću lambda izraza i uspoređivanje () statička tvornička metoda.

Pogledajmo brzi primjer kako koristiti lambda izraz za stvaranje a Usporednik:

Usporednik byRanking = (Player player1, Player player2) -> Integer.compare (player1.getRanking (), player2.getRanking ());

The Usporednik.usporedba metoda uzima metodu izračunavanja svojstva koje će se koristiti za usporedbu predmeta i vraća podudaranje Usporednik primjer:

Usporeditelj byRanking = Usporeditelj .comparing (Player :: getRanking); Comparator byAge = Comparator .comparing (Player :: getAge);

Funkcionalnost Java 8 možete detaljno istražiti u našem vodiču za usporedbu Java 8 Comparator.com.

5. Usporednik nasuprot Usporedive

The Usporedive sučelje je dobar izbor kada se koristi za definiranje zadanog redoslijeda ili, drugim riječima, ako je to glavni način usporedbe objekata.

Zatim se moramo zapitati zašto koristiti a Usporednik ako već imamo Usporedive?

Postoji nekoliko razloga zašto:

  • Ponekad ne možemo izmijeniti izvorni kod klase čije objekte želimo razvrstati, čime se koristimo Usporedive nemoguće
  • Koristeći Usporednici omogućuje nam da izbjegnemo dodavanje dodatnog koda u naše klase domene
  • Možemo definirati više različitih strategija usporedbe što nije moguće prilikom upotrebe Usporedive

6. Izbjegavanje trika oduzimanja

Tijekom ovog tutorijala koristili smo Integer.compare () metoda za usporedbu dviju cijelih brojeva. Netko bi mogao tvrditi da bismo umjesto njega trebali koristiti ovu pametnu jednoslojnu liniju:

Usporednik za usporedbu = (p1, p2) -> p1.getRanking () - p2.getRanking ();

Iako je puno sažetiji u usporedbi s drugim rješenjima, u Javi može biti žrtva prelijevanja cijelih brojeva:

Igrač player1 = novi igrač (59, "John", Integer.MAX_VALUE); Igrač igrač2 = novi igrač (67, "Roger", -1); Popis igrača = Arrays.asList (igrač1, igrač2); igrači.sort (komparator);

Budući da je -1 mnogo manje od Cijeli broj.MAX_VALUE, "Roger" bi trebao doći prije "Johna" u razvrstanoj zbirci. Međutim, zbog prelijevanja cijelih brojeva, "Integer.MAX_VALUE - (-1)" bit će manje od nule. Dakle, na temelju Usporednik / Usporediv ugovor, Cijeli broj.MAX_VALUE je manje od -1, što je očito netočno.

Stoga, unatoč onome što smo očekivali, "John" dolazi ispred "Rogera" u razvrstanoj kolekciji:

assertEquals ("John", players.get (0) .getName ()); assertEquals ("Roger", players.get (1) .getName ());

7. Zaključak

U ovom uputstvu istražili smo Usporedive i Usporednik sučelja i razgovarali o razlikama između njih.

Da biste razumjeli naprednije teme sortiranja, pogledajte naše ostale članke poput Java 8 Comparator, Java 8 Comparison with Lambdas.

I, kao i obično, izvorni kod se može naći na GitHubu.