Razvrstavanje s usporedivim i usporednim u Java

Programeri često trebaju sortirati elemente iz baze podataka u zbirku, niz ili mapu. U Javi možemo implementirati bilo koji algoritam sortiranja koji god želimo s bilo kojom vrstom. Korištenjem Comparablesučelja i compareTo()metode možemo sortirati prema abecednom redu, Stringduljini, obrnutom abecednom redu ili brojevima. ComparatorSučelje omogućuje nam da učine isto, ali na fleksibilniji način.

Što god želimo učiniti, samo moramo znati kako implementirati ispravnu logiku sortiranja za dano sučelje i tip.

Nabavite izvorni kod

Nabavite kod za ovaj Java Challenger. Možete provoditi vlastite testove dok slijedite primjere.

Sortiranje Java popisa s prilagođenim objektom

Za naš primjer koristit ćemo isti POJO koji smo do sada koristili za druge Java izazivače. U ovom prvom primjeru implementiramo usporedivo sučelje u Simpsonklasi, koristeći Simpsongenerički tip:

 class Simpson implements Comparable { String name; Simpson(String name) { this.name = name; } @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(new SimpsonCharacter("Homer ")); simpsons.add(new SimpsonCharacter("Marge ")); simpsons.add(new SimpsonCharacter("Bart ")); simpsons.add(new SimpsonCharacter("Lisa ")); Collections.sort(simpsons); simpsons.stream().map(s -> s.name).forEach(System.out::print); Collections.reverse(simpsons); simpsons.stream().forEach(System.out::print); } } 

Imajte na umu da smo nadjačali metodu compareTo () i proslijedili drugi Simpsonobjekt. Također smo nadjačali toString()metodu, samo da bismo primjer učinili lakšim za čitanje.

Na toStringmetoda pokazuje sve informacije od objekta. Kada ispisujemo objekt, izlaz će biti ono što je implementirano toString().

Metoda compareTo ()

compareTo()Metoda uspoređuje određeni objekt ili trenutnu instancu s određenom predmetu odrediti redoslijed objekata. Evo kratkog pogleda kako to compareTo()funkcionira:

  Ako se usporedba vrati

  Onda ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Možemo koristiti samo razrede koji su usporedivi s sort()metodom. Ako pokušamo proslijediti datoteku Simpsonkoja se ne implementira Comparable, primit ćemo pogrešku u kompilaciji.

sort()Metoda koristi polimorfizam propuštanjem svaki predmet koji je Comparable. Zatim će se objekti poredati prema očekivanjima.

Izlaz iz prethodnog koda bio bi:

 Bart Homer Lisa Marge 

Ako bismo željeli preokrenuti redoslijed, mogli bismo zamijeniti sort()a reverse(); iz:

 Collections.sort(simpsons); 

do:

 Collections.reverse(simpsons); 

Primjenom reverse()metode promijenio bi se prethodni izlaz u:

 Marge Lisa Homer Bart 

Razvrstavanje Java niza

U Javi možemo sortirati niz s bilo kojom vrstom koju želimo sve dok implementira Comparablesučelje. Evo primjera:

 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 = new Simpson[]{new Simpson("Lisa"), new Simpson("Homer")}; Arrays.sort(simpsons); Arrays.stream(simpsons).forEach(System.out::println); } } 

U prvom sort()pozivu niz se sortira prema:

 1 6 7 8 9 

U drugom sort()pozivu sortira se na:

 Homer Lisa 

Imajte na umu da se prilagođeni objekti moraju implementirati Comparableda bi se sortirali, čak i kao niz.

Mogu li razvrstati predmete bez usporedivog?

Da se Simpsonov objekt ne implementira Comparable, bacio bi se ClassCastException. Ako ovo pokrenete kao test, vidjet ćete nešto poput sljedećeg rezultata:

 Error:(16, 20) java: no suitable method found for sort(java.util.List) method java.util.Collections.sort(java.util.List) is not applicable (inference variable T has incompatible bounds equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson lower bounds: java.lang.Comparable) method java.util.Collections.sort(java.util.List,java.util.Comparator) is not applicable (cannot infer type-variable(s) T (actual and formal argument lists differ in length)) 

Ovaj je zapisnik možda zbunjujući, ali ne brinite. Samo imajte na umu da ClassCastExceptionće se baciti a za bilo koji razvrstani objekt koji ne implementira Comparablesučelje.

Sortiranje karte pomoću TreeMap

Java API uključuje mnoge klase za pomoć pri sortiranju, uključujući TreeMap. U donjem primjeru koristimo TreeMapza razvrstavanje ključeva u Map.

 public class TreeMapExample { public static void main(String... barney) { Map simpsonsCharacters = new TreeMap(); simpsonsCharacters.put(new SimpsonCharacter("Moe"), "shotgun"); simpsonsCharacters.put(new SimpsonCharacter("Lenny"), "Carl"); simpsonsCharacters.put(new SimpsonCharacter("Homer"), "television"); simpsonsCharacters.put(new SimpsonCharacter("Barney"), "beer"); System.out.println(simpsonsCharacters); } } 

TreeMapkoristi compareTo()metodu koju implementira Comparablesučelje. Svaki se element u rezultirajućem Maprazvrstava prema ključu. U ovom slučaju, izlaz bi bio:

 Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun 

Ipak, upamtite: ako se objekt ne implementira Comparable, ClassCastExceptionbit će bačen a.

Sortiranje seta pomoću TreeSet

SetSučelje je odgovoran za čuvanje jedinstvene vrijednosti, ali kada koristimo provedbu TreeSet, umetnuti elementi će automatski biti razvrstani kao što smo ih dodati:

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

Izlaz iz ovog koda je:

 Barney, Homer, Lenny, Moe 

Opet, ako upotrijebimo objekt koji to nije Comparable, ClassCastExceptionbacit će se a.

Razvrstavanje pomoću usporedbe

Što ako ne bismo željeli koristiti istu compareTo()metodu iz POJO klase? Možemo li nadjačati Comparablemetodu da bismo koristili drugačiju logiku? Ispod je primjer:

 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(characters); System.out.println(characters); } } 

As you can see, this code is complicated and includes a lot of repetition. We had to override the compareTo() method twice for the same logic. If there were more elements we would have to replicate the logic for each object.

Fortunately, we have the Comparator interface, which lets us detach the compareTo() logic from Java classes. Consider the same example above rewritten using Comparator:

 public class GoodExampleOfComparator { public static void main(String... args) { List characters = new ArrayList(); SimpsonCharacter homer = new SimpsonCharacter("Homer"); SimpsonCharacter moe = new SimpsonCharacter("Moe"); characters.add(homer); characters.add(moe); Collections.sort(characters, (Comparator. comparingInt(character1 -> character1.name.length()) .thenComparingInt(character2 -> character2.name.length()))); System.out.println(characters); } } 

These examples demonstrate the main difference between Comparable and Comparator.

Use Comparable when there is a single, default comparison for your object. Use Comparatorwhen you need to work around an existing compareTo(), or when you need to use specific logic in a more flexible way. Comparator detaches the sorting logic from your object and contains the compareTo() logic within your sort() method.

Using Comparator with an anonymous inner class

In this next example, we use an anonymous inner class to compare the value of objects. An anonymous inner class, in this case, is any class that implements Comparator. Using it means we are not bound to instantiating a named class implementing an interface; instead, we implement the compareTo() method inside the anonymous inner class.

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

More about inner classes

An anonymous inner class is simply any class whose name doesn’t matter, and which implements the interface we are declaring. So in the example, the new Comparator is actually the instantiation of a class that doesn’t have a name, which implements the method with the logic we want.

Using Comparator with lambda expressions

Anonymous inner classes are verbose, which can cause problems in our code. In the Comparator interface, we can use lambda expressions to simplify and make the code easier to read. For example, we could change this:

 Collections.sort(marvel, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } }); 

to this:

 Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2)); 

Less code and the same result!

The output of this code would be:

 Cyclops SpiderMan Wolverine Xavier 

We could make the code even simpler by changing this:

 Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2)); 

to this:

 Collections.sort(marvel, Comparator.naturalOrder()); 

Lambda expressions in Java

Learn more about lambda expressions and other functional programming techniques in Java.

Are the core Java classes Comparable?

Many core Java classes and objects implement the Comparable interface, which means we don’t have to implement the compareTo() logic for those classes. Here are a few familiar examples:

String

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

Integer

 public final class Integer extends Number implements Comparable { … 

Double

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

There are many others. I encourage you to explore the Java core classes to learn their important patterns and concepts.

Prihvatite usporedivi izazov sučelja!

Testirajte ono što ste naučili odgonetavanjem rezultata sljedećeg koda. Zapamtite, najbolje ćete naučiti ako ovaj izazov riješite sami proučavajući ga. Kad dođete do odgovora, odgovor možete provjeriti u nastavku. Također možete pokrenuti vlastite testove kako biste u potpunosti apsorbirali koncepte.

 public class SortComparableChallenge { public static void main(String... doYourBest) { Set set = new TreeSet(); set.add(new Simpson("Homer")); set.add(new Simpson("Marge")); set.add(new Simpson("Lisa")); set.add(new Simpson("Bart")); set.add(new Simpson("Maggie")); List list = new ArrayList(); list.addAll(set); Collections.reverse(list); list.forEach(System.out::println); } static class Simpson implements Comparable { String name; public Simpson(String name) { this.name = name; } public int compareTo(Simpson simpson) { return simpson.name.compareTo(this.name); } public String toString() { return this.name; } } } 

Koji je izlaz ovog koda?

 A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Indeterminate