Sortieren mit Comparable und Comparator in Java

Programmierer müssen häufig Elemente aus einer Datenbank in eine Sammlung, ein Array oder eine Karte sortieren. In Java können wir mit jedem Typ einen beliebigen Sortieralgorithmus implementieren. Mithilfe der ComparableSchnittstelle und der compareTo()Methode können wir nach alphabetischer Reihenfolge, StringLänge, umgekehrter alphabetischer Reihenfolge oder Zahlen sortieren . Die ComparatorSchnittstelle ermöglicht es uns, dasselbe zu tun, jedoch auf flexiblere Weise.

Was auch immer wir tun möchten, wir müssen nur wissen, wie die richtige Sortierlogik für die angegebene Schnittstelle und den angegebenen Typ implementiert wird.

Holen Sie sich den Quellcode

Holen Sie sich den Code für diesen Java Challenger. Sie können Ihre eigenen Tests ausführen, während Sie den Beispielen folgen.

Sortieren einer Java-Liste mit einem benutzerdefinierten Objekt

In unserem Beispiel verwenden wir dasselbe POJO, das wir bisher für andere Java-Herausforderer verwendet haben. In diesem ersten Beispiel implementieren wir die Schnittstelle Comparable in der SimpsonKlasse unter Verwendung Simpsondes generischen Typs:

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

Beachten Sie, dass wir die compareTo () -Methode überschrieben und ein anderes SimpsonObjekt übergeben haben. Wir haben die toString()Methode auch überschrieben , um das Beispiel besser lesbar zu machen.

Die toStringMethode zeigt alle Informationen aus dem Objekt. Wenn wir das Objekt drucken, ist die Ausgabe das, was in implementiert wurde toString().

Die compareTo () -Methode

Die compareTo()Methode vergleicht ein bestimmtes Objekt oder die aktuelle Instanz mit einem angegebenen Objekt, um die Reihenfolge der Objekte zu bestimmen. Hier ist ein kurzer Blick darauf, wie es compareTo()funktioniert:

  Wenn der Vergleich zurückkehrt

  Dann ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Wir können nur Klassen verwenden, die mit der sort()Methode vergleichbar sind . Wenn wir versuchen, eine Simpsonnicht zu implementierende zu übergeben Comparable, erhalten wir einen Kompilierungsfehler.

Die sort()Methode verwendet Polymorphismus, indem jedes Objekt übergeben wird Comparable. Die Objekte werden dann wie erwartet sortiert.

Die Ausgabe des vorherigen Codes wäre:

 Bart Homer Lisa Marge 

Wenn wir die Reihenfolge umkehren wollten, konnten wir die gegen sort()eine austauschen reverse(); von:

 Collections.sort(simpsons); 

zu:

 Collections.reverse(simpsons); 

Durch das Bereitstellen der reverse()Methode wird die vorherige Ausgabe in Folgendes geändert:

 Marge Lisa Homer Bart 

Java-Array sortieren

In Java können wir ein Array nach einem beliebigen Typ sortieren, solange es die ComparableSchnittstelle implementiert . Hier ist ein Beispiel:

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

Beim ersten sort()Aufruf wird das Array sortiert nach:

 1 6 7 8 9 

Beim zweiten sort()Aufruf wird sortiert nach:

 Homer Lisa 

Beachten Sie, dass benutzerdefinierte Objekte implementiert werden Comparablemüssen, um auch als Array sortiert zu werden.

Kann ich Objekte ohne Vergleichbar sortieren?

Wenn das Simpson-Objekt nicht implementiert wurde Comparable, wird eine ClassCastException ausgelöst. Wenn Sie dies als Test ausführen, wird etwa die folgende Ausgabe angezeigt:

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

Dieses Protokoll mag verwirrend sein, aber keine Sorge. Denken Sie daran, dass ClassCastExceptionfür jedes sortierte Objekt, das die ComparableSchnittstelle nicht implementiert , ein Wurf ausgelöst wird.

Sortieren einer Karte mit TreeMap

Die Java-API enthält viele Klassen, die beim Sortieren helfen, einschließlich TreeMap. Im folgenden Beispiel TreeMapsortieren wir Schlüssel in a 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); } } 

TreeMapverwendet die compareTo()von der ComparableSchnittstelle implementierte Methode . Jedes Element im Ergebnis Mapwird nach seinem Schlüssel sortiert. In diesem Fall wäre die Ausgabe:

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

Denken Sie jedoch daran: Wenn das Objekt nicht implementiert wird Comparable, ClassCastExceptionwird a ausgelöst.

Sortieren eines Sets mit TreeSet

Die SetSchnittstelle ist für das Speichern eindeutiger Werte verantwortlich. Wenn wir jedoch die TreeSet-Implementierung verwenden, werden eingefügte Elemente beim Hinzufügen automatisch sortiert:

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

Die Ausgabe dieses Codes lautet:

 Barney, Homer, Lenny, Moe 

Wenn wir wieder ein Objekt verwenden, das nicht ist Comparable, ClassCastExceptionwird ein geworfen.

Sortieren mit Komparator

Was wäre, wenn wir nicht dieselbe compareTo()Methode aus der POJO-Klasse verwenden wollten ? Könnten wir die ComparableMethode überschreiben , um eine andere Logik zu verwenden? Unten ist ein Beispiel:

 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.

Nehmen Sie die Herausforderung der vergleichbaren Benutzeroberfläche an!

Testen Sie, was Sie gelernt haben, indem Sie die Ausgabe des folgenden Codes herausfinden. Denken Sie daran, dass Sie am besten lernen, wenn Sie diese Herausforderung selbst lösen, indem Sie sie studieren. Sobald Sie eine Antwort erreicht haben, können Sie die Antwort unten überprüfen. Sie können auch eigene Tests durchführen, um die Konzepte vollständig zu erfassen.

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

Welches ist die Ausgabe dieses Codes?

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