Programování

Řazení pomocí srovnatelného a komparátoru v Javě

Programátoři často potřebují řadit prvky z databáze do kolekce, pole nebo mapy. V Javě můžeme s jakýmkoli typem implementovat libovolný třídicí algoritmus, který chceme. Za použití Srovnatelný rozhraní a porovnat s() metodu, můžeme třídit podle abecedního pořadí, Tětiva délka, obrácené abecední pořadí nebo čísla. The Komparátor rozhraní nám umožňuje to samé, ale flexibilnějším způsobem.

Cokoli chceme udělat, musíme jen vědět, jak implementovat správnou logiku řazení pro dané rozhraní a typ.

Získejte zdrojový kód

Získejte kód pro tento Java Challenger. Při provádění příkladů můžete spustit vlastní testy.

Třídění seznamu Java pomocí vlastního objektu

Pro náš příklad použijeme stejný POJO, jaký jsme doposud používali pro ostatní Java Challengers. V tomto prvním příkladu implementujeme srovnatelné rozhraní v Simpson třídy, pomocí Simpson v obecném typu:

 třída Simpson implementuje srovnatelný {název řetězce; Simpson (název řetězce) {this.name = name; } @Override public int compareTo (Simpson simpson) {return this.name.compareTo (simpson.name); }} veřejná třída 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")); Collections.sort (simpsonovi); simpsons.stream (). map (s -> s.name) .forEach (System.out :: print); Sbírky. Reverzní (Simpsonovi); simpsons.stream (). forEach (System.out :: print); }} 

Všimněte si, že jsme přepsali metodu compareTo () a předali jsme jinou Simpson objekt. Také jsme přepsali toString () metoda, aby se příklad lépe četl.

The toString metoda zobrazuje všechny informace z objektu. Když objekt vytiskneme, výstupem bude vše, v čem byl implementován toString ().

Metoda compareTo ()

The porovnat s() metoda porovnává daný objekt nebo aktuální instanci se zadaným objektem a určuje pořadí objektů. Zde je krátký pohled na to, jak porovnat s() práce:

Pokud se srovnání vrátí

Pak ...

  >= 1

  this.name> simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name <simpson.name

Můžeme použít pouze třídy, které jsou srovnatelné s sort () metoda. Pokud se pokusíme předat a Simpson který se neimplementuje Srovnatelný, obdržíme chybu kompilace.

The sort () metoda používá polymorfismus předáním libovolného objektu, který je Srovnatelný. Objekty pak budou tříděny podle očekávání.

Výstup z předchozího kódu by byl:

 Bart Homer Lisa Marge 

Pokud bychom chtěli obrátit pořadí, mohli bychom vyměnit sort () pro zvrátit(); z:

 Collections.sort (simpsonovi); 

na:

 Sbírky. Reverzní (Simpsonovi); 

Nasazení zvrátit() metoda by změnila předchozí výstup na:

 Marge Lisa Homer Bart 

Třídění pole Java

V Javě můžeme řadit pole s jakýmkoli typem, který chceme, pokud implementuje Srovnatelný rozhraní. Zde je pří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 (simpsonovi); Arrays.stream (simpsons) .forEach (System.out :: println); }} 

Zaprvé sort () vyvolání je pole seřazeno podle:

 1 6 7 8 9 

Ve druhém sort () vyvolání, je tříděno do:

 Homer Lisa 

Pamatujte, že je nutné implementovat vlastní objekty Srovnatelný aby bylo možné je třídit, i jako pole.

Mohu třídit objekty bez možnosti Srovnatelné?

Pokud se Simpsonův objekt neimplementoval Srovnatelný, bude vyvolána ClassCastException. Pokud to spustíte jako test, uvidíte něco jako následující výstup:

 Chyba: (16, 20) java: nebyla nalezena žádná vhodná metoda pro metodu sort (java.util.List) java.util.Collections.sort (java.util.List) není použitelná (odvozená proměnná T má nekompatibilní omezení rovnosti: com.javaworld.javachallengers.sortingcomparable.Simpson dolní hranice: java.lang.Comparable) metoda java.util.Collections.sort (java.util.List, java.util.Comparator) není použitelná (nelze odvodit typovou proměnnou (y) ) T (skutečné a formální seznamy argumentů se liší délkou)) 

Tento protokol může být matoucí, ale nebojte se. Mějte na paměti, že a ClassCastException bude vyvolána pro jakýkoli tříděný objekt, který neimplementuje Srovnatelný rozhraní.

Třídění mapy pomocí TreeMap

Rozhraní Java API zahrnuje mnoho tříd, které pomáhají při třídění, včetně TreeMap. V níže uvedeném příkladu používáme TreeMap třídit klíče do a Mapa.

 veřejná třída TreeMapExample {public static void main (String ... barney) {Map simpsonsCharacters = new TreeMap (); simpsonsCharacter.put (nový SimpsonCharacter („Moe“), „brokovnice“); simpsonsCharacter.put (nový SimpsonCharacter ("Lenny"), "Carl"); simpsonsCharacter.put (nový SimpsonCharacter ("Homer"), "televize"); simpsonsCharacter.put (nový SimpsonCharacter ("Barney"), "pivo"); System.out.println (simpsonsCharacters); }} 

TreeMap používá porovnat s() metoda implementovaná Srovnatelný rozhraní. Každý prvek ve výsledném Mapa je tříděno podle klíče. V tomto případě by výstup byl:

 Barney = pivo, Homer = televize, Lenny = Carl, Moe = brokovnice 

Nezapomeňte však: pokud se objekt neimplementuje Srovnatelný, a ClassCastException bude hozen.

Třídění sady pomocí TreeSet

The Soubor rozhraní je zodpovědné za ukládání jedinečných hodnot, ale když použijeme implementaci TreeSet, vložené prvky se automaticky seřadí, jak je přidáme:

 veřejná třída 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 tohoto kódu je:

 Barney, Homer, Lenny, Moe 

Opět platí, že pokud použijeme objekt, který není Srovnatelný, a ClassCastException bude hozen.

Třídění pomocí komparátoru

Co kdybychom nechtěli použít totéž porovnat s() metoda ze třídy POJO? Mohli bychom přepsat Srovnatelný metoda použít jinou logiku? Níže je uveden příklad:

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

Jak vidíte, tento kód je komplikovaný a zahrnuje spoustu opakování. Museli jsme přepsat porovnat s() metoda dvakrát pro stejnou logiku. Pokud by existovalo více prvků, museli bychom replikovat logiku pro každý objekt.

Naštěstí máme rozhraní komparátoru, které nám umožňuje odpojit porovnat s() logika z tříd Java. Zvažte stejný příklad výše přepsaný pomocí 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"); characters.add (homer); characters.add (moe); Collections.sort (znaky, (komparátor. PorovnáníInt (znak1 -> znak1.nazev.lenku ()) .thenComparingInt (znak2 -> znak2.nazev.lenku ()))); System.out.println (znaky); }} 

Tyto příklady ukazují hlavní rozdíl mezi Srovnatelný a Komparátor.

Použití Srovnatelný když pro váš objekt existuje jediné výchozí porovnání. Použití Komparátorkdyž potřebujete obejít existující porovnat s(), nebo když potřebujete použít konkrétní logiku flexibilnějším způsobem. Komparátor odpojí logiku třídění od vašeho objektu a obsahuje porovnat s() logika ve vašem sort () metoda.

Použití komparátoru s anonymní vnitřní třídou

V tomto dalším příkladu použijeme anonymní vnitřní třídu k porovnání hodnoty objektů. An anonymní vnitřní třída, v tomto případě je jakákoli třída, která implementuje Komparátor. Použití to znamená, že nejsme vázáni k vytvoření instance pojmenované třídy implementující rozhraní; místo toho implementujeme porovnat s() metoda uvnitř anonymní vnitřní třídy.

 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); }} 

Více o vnitřních třídách

An anonymní vnitřní třída je prostě jakákoli třída, na jejímž jménu nezáleží, a která implementuje rozhraní, které deklarujeme. Takže v příkladu nový Komparátor je vlastně instance třídy, která nemá název, což implementuje metodu s logikou, kterou chceme.

Použití komparátoru s výrazy lambda

Anonymní vnitřní třídy jsou podrobné, což může v našem kódu způsobit problémy. V Komparátor rozhraní můžeme použít výrazy lambda ke zjednodušení a usnadnění čtení kódu. Mohli bychom to například změnit:

 Collections.sort (zázrak, nový komparátor () {@Override public int porovnání (řetězec hrdina1, řetězec hrdina2) {návrat hero1.compareTo (hrdina2);}}); 

k tomuto:

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

Méně kódu a stejný výsledek!

Výstup tohoto kódu by byl:

 Cyclops SpiderMan Wolverine Xavier 

Změnou tohoto kódu bychom mohli ještě zjednodušit kód:

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

k tomuto:

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

Lambda výrazy v Javě

Další informace o výrazech lambda a dalších technikách funkčního programování v jazyce Java.

Jsou základní třídy Java srovnatelné?

Mnoho základních tříd a objektů Java implementuje Srovnatelný což znamená, že nemusíme implementovat porovnat s() logika pro tyto třídy. Zde je několik známých příkladů:

Tětiva

 public final class String implements java.io.Serializable, Comparable, CharSequence {... 

Celé číslo

 public final class Integer extends Number implements Comparable {… 

Dvojnásobek

 public final class Double extends Number implements Comparable {... 

Existuje mnoho dalších. Doporučuji vám prozkoumat základní třídy Java a naučit se jejich důležité vzorce a koncepty.

Přijměte výzvu Srovnatelné rozhraní!

Vyzkoušejte, co jste se naučili, a zjistěte výstup následujícího kódu. Nezapomeňte, že se nejlépe naučíte, pokud tuto výzvu vyřešíte sami tím, že si ji prostudujete. Jakmile dosáhnete odpovědi, můžete ji zkontrolovat níže. Můžete také spustit vlastní testy, abyste plně absorbovali koncepty.

 veřejná třída 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“)); Seznam seznamu = nový ArrayList (); list.addAll (sada); Sbírka.Reverzní (seznam); list.forEach (System.out :: println); } statická třída Simpson implementuje srovnatelný {název řetězce; public Simpson (název řetězce) {this.name = name; } public int compareTo (Simpson simpson) {return simpson.name.compareTo (this.name); } public String toString () {return this.name; }}} 

Jaký je výstup tohoto 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