Sorteren met Comparable en Comparator in Java

Programmeurs moeten vaak elementen uit een database sorteren in een verzameling, array of kaart. In Java kunnen we elk soort sorteeralgoritme implementeren dat we willen met elk type. Met behulp van de Comparableinterface en compareTo()methode kunnen we sorteren op alfabetische volgorde, Stringlengte, omgekeerde alfabetische volgorde of cijfers. De Comparatorinterface stelt ons in staat hetzelfde te doen, maar op een meer flexibele manier.

Wat we ook willen doen, we hoeven alleen maar te weten hoe we de juiste sorteerlogica voor de gegeven interface en type moeten implementeren.

Haal de broncode op

Verkrijg de code voor deze Java Challenger. U kunt uw eigen tests uitvoeren terwijl u de voorbeelden volgt.

Een Java-lijst sorteren met een aangepast object

Voor ons voorbeeld gebruiken we dezelfde POJO die we tot nu toe voor andere Java Challengers hebben gebruikt. In dit eerste voorbeeld implementeren we de vergelijkbare interface in de Simpsonklas, met behulp Simpsonvan het generieke type:

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

Merk op dat we de methode CompareTo () hebben overschreven en een ander Simpsonobject hebben doorgegeven . We hebben ook de toString()methode overschreven , alleen om het voorbeeld leesbaarder te maken.

De toStringmethode toont alle informatie van het object. Wanneer we het object afdrukken, is de uitvoer datgene waarin is geïmplementeerd toString().

De methode CompareTo ()

De compareTo()methode vergelijkt een bepaald object of de huidige instantie met een opgegeven object om de volgorde van objecten te bepalen. Hier is een korte blik op hoe compareTo()werkt:

  Als de vergelijking terugkeert

  Vervolgens ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

We kunnen alleen klassen gebruiken die vergelijkbaar zijn met de sort()methode. Als we proberen een door te geven Simpsondie niet wordt geïmplementeerd Comparable, ontvangen we een compilatiefout.

De sort()methode maakt gebruik van polymorfisme door elk object door te geven Comparable. Objecten worden dan gesorteerd zoals verwacht.

De uitvoer van de vorige code zou zijn:

 Bart Homer Lisa Marge 

Als we de volgorde wilden omdraaien, konden we de omwisselen sort()voor een reverse(); van:

 Collections.sort(simpsons); 

naar:

 Collections.reverse(simpsons); 

Als u de reverse()methode implementeert , wordt de vorige uitvoer gewijzigd in:

 Marge Lisa Homer Bart 

Een Java-array sorteren

In Java kunnen we een array sorteren met elk type dat we willen, zolang het de Comparableinterface implementeert . Hier is een voorbeeld:

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

Bij de eerste sort()aanroep wordt de array gesorteerd naar:

 1 6 7 8 9 

In de tweede sort()aanroep wordt het gesorteerd op:

 Homer Lisa 

Houd er rekening mee dat aangepaste objecten moeten worden geïmplementeerd Comparableom te worden gesorteerd, zelfs als een array.

Kan ik objecten sorteren zonder vergelijkbaar?

Als het Simpson-object niet werd geïmplementeerd Comparable, zou een ClassCastException worden gegenereerd. Als u dit als een test uitvoert, ziet u zoiets als de volgende uitvoer:

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

Dit logboek kan verwarrend zijn, maar maak u geen zorgen. Houd er rekening mee dat er een ClassCastExceptionwordt gegenereerd voor elk gesorteerd object dat de Comparableinterface niet implementeert .

Een kaart sorteren met TreeMap

De Java API bevat veel klassen om te helpen bij het sorteren, waaronder TreeMap. In het onderstaande voorbeeld gebruiken TreeMapwe om sleutels in een 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); } } 

TreeMapgebruikt de compareTo()methode geïmplementeerd door de Comparableinterface. Elk element in het resultaat Mapwordt gesorteerd op zijn sleutel. In dit geval zou de output zijn:

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

Onthoud echter: als het object niet wordt geïmplementeerd Comparable, ClassCastExceptionwordt er een gegenereerd.

Een set sorteren met TreeSet

De Setinterface is verantwoordelijk voor het opslaan van unieke waarden, maar wanneer we de TreeSet-implementatie gebruiken, worden ingevoegde elementen automatisch gesorteerd als we ze toevoegen:

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

De output van deze code is:

 Barney, Homer, Lenny, Moe 

Nogmaals, als we een object gebruiken dat dat niet is Comparable, ClassCastExceptionwordt er een geworpen.

Sorteren met Comparator

Wat als we niet dezelfde compareTo()methode uit de POJO-klasse wilden gebruiken ? Kunnen we de Comparablemethode overschrijven om een ​​andere logica te gebruiken? Hieronder een voorbeeld:

 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.

Ga de vergelijkbare interface-uitdaging aan!

Test wat je hebt geleerd door de uitvoer van de volgende code te achterhalen. Onthoud dat je het beste leert als je deze uitdaging voor jezelf oplost door hem gewoon te bestuderen. Zodra u een antwoord heeft bereikt, kunt u het onderstaande antwoord bekijken. U kunt ook uw eigen tests uitvoeren om de concepten volledig op te nemen.

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

Wat is de output van deze code?

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