Сортировка с помощью Comparable и Comparator в Java

Программистам часто требуется отсортировать элементы из базы данных в коллекцию, массив или карту. В Java мы можем реализовать любой алгоритм сортировки любого типа. Используя Comparableинтерфейс и compareTo()метод, мы можем сортировать, используя алфавитный порядок, Stringдлину, обратный алфавитный порядок или числа. ComparatorИнтерфейс позволяет делать то же самое , но в более гибкой форме.

Что бы мы ни хотели делать, нам просто нужно знать, как реализовать правильную логику сортировки для данного интерфейса и типа.

Получите исходный код

Получите код для этого Java Challenger. Вы можете запускать свои собственные тесты, следуя примерам.

Сортировка списка Java с помощью настраиваемого объекта

В нашем примере мы будем использовать тот же POJO, который мы использовали для других Java Challenger. В этом первом примере мы реализуем интерфейс Comparable в Simpsonклассе, используя Simpsonв универсальном типе:

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

Обратите внимание, что мы переопределили метод compareTo () и передали другой Simpsonобъект. Мы также переопределили toString()метод, чтобы облегчить чтение примера.

В toStringметоде показывает всю информацию от объекта. Когда мы печатаем объект, на выходе будет то, что было реализовано toString().

Метод compareTo ()

compareTo()Метод сравнивает данный объект или текущий экземпляр с указанным объектом , чтобы определить порядок объектов. Вот краткий обзор того, как это compareTo()работает:

  Если сравнение вернет

  Потом ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Мы можем использовать только классы, сопоставимые с sort()методом. Если мы попытаемся передать Simpson, который не реализуется Comparable, мы получим ошибку компиляции.

sort()Метод использует полиморфизм, передавая любой объект , который Comparable. Затем объекты будут отсортированы должным образом.

Результатом предыдущего кода будет:

 Bart Homer Lisa Marge 

Если бы мы хотели изменить порядок, мы могли бы заменить его sort()на a reverse(); из:

 Collections.sort(simpsons); 

кому:

 Collections.reverse(simpsons); 

Развертывание reverse()метода изменит предыдущий вывод на:

 Marge Lisa Homer Bart 

Сортировка массива Java

В Java мы можем сортировать массив любого типа, который нам нужен, если он реализует Comparableинтерфейс. Вот пример:

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

При первом sort()вызове массив сортируется по:

 1 6 7 8 9 

Во втором sort()вызове он сортируется по:

 Homer Lisa 

Имейте в виду, что настраиваемые объекты должны быть реализованы Comparableдля сортировки, даже в виде массива.

Могу ли я сортировать объекты без Comparable?

Если объект Simpson не реализовывался Comparable, было бы выброшено исключение ClassCastException. Если вы запустите это как тест, вы увидите что-то вроде следующего вывода:

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

Этот журнал может сбивать с толку, но не волнуйтесь. Просто имейте в виду, что ClassCastExceptionдля любого отсортированного объекта, который не реализует Comparableинтерфейс, будет выдан a .

Сортировка карты с помощью TreeMap

API Java включает в себя множество классов для помощи в сортировке, включая TreeMap. В приведенном ниже примере мы используем TreeMapдля сортировки ключей в 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); } } 

TreeMapиспользует compareTo()метод, реализованный в Comparableинтерфейсе. Каждый элемент в результате Mapсортируется по ключу. В этом случае вывод будет:

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

Однако помните: если объект не реализуется Comparable, ClassCastExceptionбудет выброшено.

Сортировка набора с помощью TreeSet

SetИнтерфейс отвечает за хранение уникальных значений, но когда мы используем реализацию TreeSet, вставленные элементы будут автоматически сортируются , как мы добавим их:

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

Результатом этого кода является:

 Barney, Homer, Lenny, Moe 

Опять же, если мы используем объект, которого нет Comparable, ClassCastExceptionбудет выброшено.

Сортировка с помощью компаратора

Что, если мы не хотим использовать тот же compareTo()метод из класса POJO? Можем ли мы переопределить Comparableметод, чтобы использовать другую логику? Ниже приведен пример:

 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.

Примите вызов Comparable interface!

Проверьте то, что вы узнали, вычислив вывод следующего кода. Помните, вы научитесь лучше, если решите эту задачу для себя, просто изучая ее. Получив ответ, вы можете проверить ответ ниже. Вы также можете запустить свои собственные тесты, чтобы полностью усвоить концепции.

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

Каков результат этого кода?

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