Programovanie

Triedenie s porovnateľnými a komparátormi v Jave

Programátori musia často triediť prvky z databázy do kolekcie, poľa alebo mapy. V Jave môžeme implementovať akýkoľvek triediaci algoritmus, ktorý chceme, s akýmkoľvek typom. Pomocou Porovnateľné rozhranie a porovnať s() metódou môžeme triediť podľa abecedného poradia, String dĺžka, obrátené abecedné poradie alebo čísla. The Komparátor Rozhranie nám umožňuje robiť to isté, ale flexibilnejším spôsobom.

Čokoľvek chceme urobiť, musíme len vedieť, ako implementovať správnu logiku triedenia pre dané rozhranie a typ.

Získajte zdrojový kód

Získajte kód pre tento Java Challenger. Podľa príkladov môžete spustiť svoje vlastné testy.

Triedenie zoznamu Java List s vlastným objektom

Pre náš príklad použijeme rovnaký POJO, aký sme doteraz používali pre iných Java Challengerov. V tomto prvom príklade implementujeme porovnateľné rozhranie v Simpson triedy, pomocou Simpson v generickom type:

 trieda Simpson implementuje Comparable {Názov reťazca; Simpson (názov reťazca) {this.name = meno; } @Override public int compareTo (Simpson simpson) {return this.name.compareTo (simpson.name); }} public class SimpsonSorting {public static void main (String ... sortingWithList) {List simpsons = new ArrayList (); simpsons.add (nový SimpsonCharacter ("Homer")); simpsons.add (nový SimpsonCharacter („Marge“)); simpsons.add (nový SimpsonCharacter („Bart“)); simpsons.add (nový SimpsonCharacter ("Lisa")); Zbierka.trieda (simpsonovci); simpsons.stream (). map (s -> s.name) .forEach (System.out :: print); Zbierky.Reverzný (Simpsonovi); simpsons.stream (). forEach (System.out :: print); }} 

Upozorňujeme, že sme prepísali metódu compareTo () a odovzdali sme inú Simpson objekt. Tiež sme prepísali natiahnuť() metóda, len aby sa príklad ľahšie čítal.

The natiahnuť metóda zobrazuje všetky informácie z objektu. Keď objekt vytlačíme, výstupom bude všetko, v čom bol implementovaný natiahnuť().

Metóda compareTo ()

The porovnať s() metóda porovnáva daný objekt alebo aktuálnu inštanciu so zadaným objektom a určuje tak poradie objektov. Tu je rýchly pohľad na to, ako porovnať s() Tvorba:

Ak sa porovnanie vráti

Potom ...

  >= 1

  this.name> simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name <simpson.name

Môžeme použiť iba triedy, ktoré sú porovnateľné s sort () metóda. Ak sa pokúsime prejsť a Simpson ktoré sa nerealizuje Porovnateľné, dostaneme chybu kompilácie.

The sort () metóda využíva polymorfizmus odovzdaním ľubovoľného objektu, ktorý je Porovnateľné. Objekty sa potom zoradia podľa očakávania.

Výstup z predchádzajúceho kódu by bol:

 Bart Homer Lisa Marge 

Ak by sme chceli obrátiť poradie, mohli by sme vymeniť sort () pre reverz (); od:

 Zbierka.trieda (simpsonovci); 

do:

 Zbierky.Reverzný (Simpsonovci); 

Nasadzovanie reverz () metóda zmení predchádzajúci výstup na:

 Marge Lisa Homer Bart 

Triedenie poľa Java

V Jave môžeme triediť pole s akýmkoľvek typom, ktorý chceme, pokiaľ implementuje Porovnateľné rozhranie. Tu je príklad:

 public class ArraySorting {public static void main (String ... moeTavern) {int [] moesPints ​​= new int [] {9, 8, 7, 6, 1}; Arrays.sort (moesPints); Arrays.stream (moesPints) .forEach (System.out :: print); Simpson [] simpsons = nový Simpson [] {nový Simpson („Lisa“), nový Simpson („Homer“)}; Array.sort (simpsonovci); Arrays.stream (simpsons) .forEach (System.out :: println); }} 

V prvom sort () vyvolanie, pole je zoradené podľa:

 1 6 7 8 9 

V druhom sort () vyvolanie, je zoradené podľa:

 Homer Lisa 

Majte na pamäti, že vlastné objekty sa musia implementovať Porovnateľné aby bolo možné ich triediť, dokonca aj ako pole.

Môžem triediť objekty bez možnosti Porovnateľné?

Ak sa Simpsonov objekt neimplementoval Porovnateľné, bola by vyhodená ClassCastException. Ak to spustíte ako test, uvidíte niečo ako nasledujúci výstup:

 Chyba: (16, 20) java: nenašla sa žiadna vhodná metóda pre metódu sort (java.util.List) java.util.Collections.sort (java.util.List) nie je použiteľná (odvodená premenná T má nekompatibilné obmedzenia rovnosti: com.javaworld.javachallengers.sortingcomparable.Simpson spodné hranice: java.lang.Comparable) metóda java.util.Collections.sort (java.util.List, java.util.Comparator) nie je použiteľná (nemôže odvodiť typovú premennú (y) ) T (skutočné a formálne zoznamy argumentov sa líšia v dĺžke)) 

Tento denník môže byť mätúci, ale nemusíte sa obávať. Len majte na pamäti, že a ClassCastException bude vyhodený pre akýkoľvek triedený objekt, ktorý neimplementuje Porovnateľné rozhranie.

Triedenie mapy pomocou TreeMap

Rozhranie Java API obsahuje mnoho tried na pomoc s triedením, vrátane TreeMap. V príklade nižšie použijeme TreeMap triediť kľúče do a Mapa.

 verejná trieda TreeMapExample {public static void main (String ... barney) {Map simpsonsCharacters = new TreeMap (); simpsonsCharacter.put (nový SimpsonCharacter („Moe“), „brokovnica“); simpsonsCharacter.put (nový SimpsonCharacter ("Lenny"), "Carl"); simpsonsCharacter.put (nový SimpsonCharacter („Homer“), „televízia“); simpsonsCharacter.put (nový SimpsonCharacter („Barney“), „pivo“); System.out.println (simpsonsCharacters); }} 

TreeMap používa porovnať s() metóda implementovaná Porovnateľné rozhranie. Každý prvok vo výslednom Mapa je zoradený podľa kľúča. V takom prípade by bol výstup:

 Barney = pivo, Homer = televízia, Lenny = Carl, Moe = brokovnica 

Pamätajte však: ak sa objekt neimplementuje Porovnateľné, a ClassCastException bude vyhodený.

Triedenie sady pomocou TreeSet

The Nastaviť rozhranie je zodpovedné za ukladanie jedinečných hodnôt, ale keď použijeme implementáciu TreeSet, vložené prvky sa automaticky zoradia, keď ich pridáme:

 verejná trieda TreeSetExample {public static void main (String ... barney) {Set simpsonsCharacters = new TreeSet (); simpsonsCharacter.add (nový SimpsonCharacter ("Moe")); simpsonsCharacter.add (nový SimpsonCharacter ("Lenny")); simpsonsCharacter.add (nový SimpsonCharacter ("Homer")); simpsonsCharacter.add (nový SimpsonCharacter ("Barney")); System.out.println (simpsonsCharacters); }} 

Výstup z tohto kódu je:

 Barney, Homer, Lenny, Moe 

Opäť platí, že ak použijeme objekt, ktorý nie je Porovnateľné, a ClassCastException bude vyhodený.

Triedenie pomocou komparátora

Čo ak by sme nechceli používať to isté porovnať s() metóda z triedy POJO? Mohli by sme prepísať Porovnateľné metóda na použitie inej logiky? Nižšie je uvedený príklad:

 public class BadExampleOfComparable {public static void main (String ... args) {List characters = new ArrayList (); SimpsonCharacter homer = nový SimpsonCharacter ("Homer") {@Override public int compareTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }}; SimpsonCharacter moe = nový SimpsonCharacter ("Moe") {@Override public int compareTo (SimpsonCharacter simpson) {return this.name.length () - (simpson.name.length ()); }}; znaky.pridat (homer); znaky.pridat (moe); Zbierka.sort (znaky); System.out.println (znaky); }} 

Ako vidíte, tento kód je komplikovaný a obsahuje veľa opakovaní. Museli sme prepísať porovnať s() metóda dvakrát pre rovnakú logiku. Keby bolo viac prvkov, museli by sme replikovať logiku pre každý objekt.

Našťastie máme rozhranie komparátora, ktoré nám umožňuje odpojiť porovnať s() logika z Java tried. Zvážte ten istý príklad prepísaný pomocou Komparátor:

 public class GoodExampleOfComparator {public static void main (String ... args) {List characters = new ArrayList (); SimpsonCharacter homer = nový SimpsonCharacter ("Homer"); SimpsonCharacter moe = nový SimpsonCharacter ("Moe"); znaky.pridat (homer); znaky.pridat (moe); Collections.sort (znaky, (komparátor. ComparingInt (znak1 -> znak1.meno.lenka ()) .thenComparingInt (znak2 -> znak2.meno.lenka ()))); System.out.println (znaky); }} 

Tieto príklady demonštrujú hlavný rozdiel medzi Porovnateľné a Komparátor.

Použite Porovnateľné ak pre váš objekt existuje jediné predvolené porovnanie. Použite Komparátorkeď potrebujete obísť existujúcu porovnať s(), alebo keď potrebujete flexibilnejšie použiť konkrétnu logiku. Komparátor odpojí logiku triedenia od vášho objektu a obsahuje porovnať s() logika vo vašom sort () metóda.

Používanie komparátora s anonymnou vnútornou triedou

V tomto ďalšom príklade používame anonymnú vnútornú triedu na porovnanie hodnoty objektov. An anonymná vnútorná trieda, v tomto prípade je akákoľvek trieda, ktorá implementuje Komparátor. Jeho použitie znamená, že nie sme povinní vytvoriť inštanciu pomenovanej triedy implementujúcej rozhranie; namiesto toho implementujeme porovnať s() metóda vo vnútri anonymnej vnútornej triedy.

 public class MarvelComparator {public static void main (String ... comparator) {List marvelHeroes = new ArrayList (); marvelHeroes.add ("SpiderMan"); marvelHeroes.add ("Wolverine"); marvelHeroes.add ("Xavier"); marvelHeroes.add ("Cyclops"); Collections.sort (marvelHeroes, new Comparator () {@Override public int compare (String hero1, String hero2) {return hero1.compareTo (hero2);}}); Collections.sort (marvelHeroes, (m1, m2) -> m1.compareTo (m2)); Collections.sort (marvelHeroes, Comparator.naturalOrder ()); marvelHeroes.forEach (System.out :: print); }} 

Viac o vnútorných triedach

An anonymná vnútorná trieda je jednoducho akákoľvek trieda, na ktorej mene nezáleží, a ktorá implementuje rozhranie, ktoré deklarujeme. Takže v príklade nový Komparátor je vlastne inštancia triedy, ktorá nemá názov, ktorá implementuje metódu s požadovanou logikou.

Používanie komparátora s výrazmi lambda

Anonymné vnútorné triedy sú podrobné, čo môže v našom kóde spôsobiť problémy. V Komparátor rozhrania môžeme na zjednodušenie a uľahčenie čítania kódu použiť výrazy lambda. Mohli by sme to napríklad zmeniť:

 Collections.sort (zázrak, nový komparátor () {@Override public int compare (reťazec hrdina1, reťazec hrdina2) {návrat hero1.compareTo (hrdina2);}}); 

do tohto:

 Zbierky.sort (zázrak, (m1, m2) -> m1.compareTo (m2)); 

Menej kódu a rovnaký výsledok!

Výstup tohto kódu by bol:

 Cyclops SpiderMan Wolverine Xavier 

Zmenou tohto kódu by sme mohli zjednodušiť kód:

 Zbierky.sort (zázrak, (m1, m2) -> m1.compareTo (m2)); 

do tohto:

 Collections.sort (zázrak, Comparator.naturalOrder ()); 

Výrazy lambda v jazyku Java

Získajte viac informácií o výrazoch lambda a ďalších technikách funkčného programovania v jazyku Java.

Sú základné triedy Java porovnateľné?

Mnoho základných tried a objektov Java implementuje Porovnateľné čo znamená, že nemusíme implementovať porovnať s() logika pre tieto triedy. Tu je niekoľko známych príkladov:

String

 verejná konečná trieda String implementuje java.io.Serializable, Comparable, CharSequence {... 

Celé číslo

 verejná koncová trieda Celé číslo sa rozširuje Počet implementuje porovnateľné {… 

Dvojitý

 verejná finálna trieda Double extends Number implements Comparable {... 

Je veľa ďalších. Odporúčam vám preskúmať základné triedy Java, aby ste sa naučili ich dôležité vzorce a koncepty.

Prijmite výzvu porovnateľného rozhrania!

Vyskúšajte, čo ste sa naučili, zistením výstupu z nasledujúceho kódu. Pamätajte, že najlepšie sa naučíte, ak túto výzvu vyriešite sami tým, že si ju naštudujete. Po dosiahnutí odpovede si môžete skontrolovať odpoveď nižšie. Môžete tiež spustiť svoje vlastné testy, aby ste koncepty naplno absorbovali.

 public class SortComparableChallenge {public static void main (String ... doYourBest) {Set set = new TreeSet (); set.add (nový Simpson („Homer“)); set.add (nový Simpson („Marge“)); set.add (nový Simpson („Lisa“)); set.add (nový Simpson („Bart“)); set.add (nový Simpson („Maggie“)); Zoznam zoznam = nový ArrayList (); list.addAll (sada); Zbierky.Reverzný (zoznam); list.forEach (System.out :: println); } statická trieda Simpson implementuje Comparable {Názov reťazca; public Simpson (názov reťazca) {this.name = meno; } public int compareTo (Simpson simpson) {return simpson.name.compareTo (this.name); } public String toString () {return this.name; }}} 

Aký je výstup tohto kódu?

 A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Neurčité 
$config[zx-auto] not found$config[zx-overlay] not found