Ordinamento con Comparable e Comparator in Java

I programmatori hanno spesso bisogno di ordinare gli elementi da un database in una raccolta, un array o una mappa. In Java, possiamo implementare qualsiasi algoritmo di ordinamento che desideriamo con qualsiasi tipo. Utilizzando l' Comparableinterfaccia e il compareTo()metodo, possiamo ordinare in ordine alfabetico, Stringlunghezza, ordine alfabetico inverso o numeri. L' Comparatorinterfaccia ci permette di fare lo stesso ma in modo più flessibile.

Qualunque cosa vogliamo fare, dobbiamo solo sapere come implementare la logica di ordinamento corretta per l'interfaccia e il tipo dati.

Ottieni il codice sorgente

Ottieni il codice per questo Java Challenger. Puoi eseguire i tuoi test mentre segui gli esempi.

Ordinamento di un elenco Java con un oggetto personalizzato

Per il nostro esempio useremo lo stesso POJO che abbiamo usato finora per altri Java Challenger. In questo primo esempio, implementiamo l'interfaccia Comparable nella Simpsonclasse, utilizzando Simpsonnel tipo generico:

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

Nota che abbiamo sovrascritto il metodo compareTo () e passato un altro Simpsonoggetto. Abbiamo anche ignorato il toString()metodo, solo per rendere l'esempio più facile da leggere.

Il toStringmetodo mostra tutte le informazioni dall'oggetto. Quando stampiamo l'oggetto, l'output sarà quello che è stato implementato in toString().

Il metodo compareTo ()

Il compareTo()metodo confronta un determinato oggetto o l'istanza corrente con un oggetto specificato per determinare l'ordine degli oggetti. Ecco una rapida occhiata a come compareTo()funziona:

  Se il confronto ritorna

  Poi ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Possiamo usare solo classi confrontabili con il sort()metodo. Se proviamo a passare un messaggio Simpsonche non implementa Comparable, riceveremo un errore di compilazione.

Il sort()metodo utilizza il polimorfismo passando qualsiasi oggetto che sia Comparable. Gli oggetti verranno quindi ordinati come previsto.

L'output del codice precedente sarebbe:

 Bart Homer Lisa Marge 

Se volessimo invertire l'ordine, potremmo scambiare il sort()per a reverse(); a partire dal:

 Collections.sort(simpsons); 

per:

 Collections.reverse(simpsons); 

La distribuzione del reverse()metodo cambierebbe l'output precedente in:

 Marge Lisa Homer Bart 

Ordinamento di un array Java

In Java, possiamo ordinare un array con qualsiasi tipo desideriamo purché implementi l' Comparableinterfaccia. Ecco un esempio:

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

Nella prima sort()chiamata, l'array è ordinato in:

 1 6 7 8 9 

Nella seconda sort()chiamata, viene ordinato in:

 Homer Lisa 

Tieni presente che gli oggetti personalizzati devono essere implementati Comparableper essere ordinati, anche come array.

Posso ordinare gli oggetti senza Comparable?

Se l'oggetto Simpson non fosse implementato Comparable, verrebbe generata un'eccezione ClassCastException. Se lo esegui come test, vedrai qualcosa di simile al seguente output:

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

Questo registro potrebbe creare confusione, ma non preoccuparti. Tieni presente che ClassCastExceptionverrà lanciato un per qualsiasi oggetto ordinato che non implementa l' Comparableinterfaccia.

Ordinamento di una mappa con TreeMap

L'API Java include molte classi per assistere con l'ordinamento, incluso TreeMap. Nell'esempio seguente, usiamo TreeMapper ordinare le chiavi in ​​un file 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); } } 

TreeMaputilizza il compareTo()metodo implementato Comparabledall'interfaccia. Ogni elemento nel risultato Mapè ordinato in base alla sua chiave. In questo caso, l'output sarebbe:

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

Ricorda, però: se l'oggetto non implementa Comparable, ClassCastExceptionverrà lanciato un.

Ordinamento di un set con TreeSet

L' Setinterfaccia è responsabile della memorizzazione di valori univoci, ma quando usiamo l'implementazione TreeSet, gli elementi inseriti verranno automaticamente ordinati man mano che li aggiungiamo:

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

L'output di questo codice è:

 Barney, Homer, Lenny, Moe 

Di nuovo, se usiamo un oggetto che non lo è Comparable, ClassCastExceptionverrà lanciato a.

Ordinamento con comparatore

E se non volessimo utilizzare lo stesso compareTo()metodo della classe POJO? Potremmo ignorare il Comparablemetodo per utilizzare una logica diversa? Di seguito è riportato un esempio:

 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.

Accetta la sfida dell'interfaccia comparabile!

Verifica ciò che hai imparato scoprendo l'output del codice seguente. Ricorda, imparerai meglio se risolverai questa sfida da solo studiandola. Una volta raggiunta una risposta, puoi controllare la risposta di seguito. Puoi anche eseguire i tuoi test per assorbire completamente i concetti.

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

Qual è l'output di questo codice?

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