Comparator и Comparable в Java

Comparator и Comparable в Java

1. Вступление

Сравнения на Java довольно просты - пока это не так.

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

2. Настройка примера

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

Начнем с создания простого классаPlayer:

public class Player {
    private int ranking;
    private String name;
    private int age;

    // constructor, getters, setters
}

Затем давайте создадим классPlayerSorter для создания нашей коллекции и попытаемся отсортировать ее с помощьюCollections.sort:

public static void main(String[] args) {
    List footballTeam = new ArrayList<>();
    Player player1 = new Player(59, "John", 20);
    Player player2 = new Player(67, "Roger", 22);
    Player player3 = new Player(45, "Steven", 24);
    footballTeam.add(player1);
    footballTeam.add(player2);
    footballTeam.add(player3);

    System.out.println("Before Sorting : " + footballTeam);
    Collections.sort(footballTeam);
    System.out.println("After Sorting : " + footballTeam);
}

Здесь, как и ожидалось, это приводит к ошибке во время компиляции:

The method sort(List) in the type Collections
  is not applicable for the arguments (ArrayList)

Давайте разберемся, что мы здесь сделали не так.

3. Comparableс

Как следует из названия,Comparable is an interface defining a strategy of comparing an object with other objects of the same type. This is called the class’s “natural ordering”.

Соответственно, чтобы иметь возможность сортировать - мы должны определить наш объектPlayer как сопоставимый, реализовав интерфейсComparable:

public class Player implements Comparable {

    //...
    @Override
    public int compareTo(Player otherPlayer) {
        return (this.getRanking() - otherPlayer.getRanking());
    }
}

Порядок сортировки определяется возвращаемым значениемcompareTo() метод.

Метод возвращает число, указывающее, является ли сравниваемый объект меньше, равен или больше объекта, передаваемого в качестве аргумента.

Наконец, когда мы сейчас запускаем нашPlayerSorter, мы можем видеть нашиPlayers, отсортированные по их ранжированию:

Before Sorting : [John, Roger, Steven]
After Sorting : [Steven, John, Roger]

Теперь, когда у нас есть четкое понимание естественного упорядочения с помощьюComparable, давайте посмотрим наhow we can use other types of ordering, in a more flexible manner, а не непосредственно на реализацию интерфейса.

4. Comparatorс

The Comparator interface defines a compare(arg1, arg2) method с двумя аргументами, которые представляют сравниваемые объекты и работает аналогично методуComparable.compareTo().

4.1. СозданиеComparators

Чтобы создатьComparator,, мы должны реализовать интерфейсComparator.

В нашем первом примере мы создадимComparator, чтобы использовать атрибутranking элементаPlayer для сортировки игроков:

public class PlayerRankingComparator implements Comparator {

    @Override
    public int compare(Player firstPlayer, Player secondPlayer) {
       return (firstPlayer.getRanking() - secondPlayer.getRanking());
    }
}

Точно так же мы можем создатьComparator, чтобы использовать атрибутagePlayer для сортировки игроков:

public class PlayerAgeComparator implements Comparator {
    @Override
    public int compare(Player firstPlayer, Player secondPlayer) {
       return (firstPlayer.getAge() - secondPlayer.getAge());
    }
}

4.2. Comparators в действии

Чтобы продемонстрировать концепцию, давайте изменим нашPlayerSorter, введя второй аргумент дляCollections.sort method, который на самом деле является экземпляромComparator, который мы хотим использовать.

Using this approach, we can override the natural ordering:

PlayerRankingComparator playerComparator = new PlayerRankingComparator();
Collections.sort(footballTeam, playerComparator);

Теперь давайте запустим нашPlayerRankingSorter to и посмотрим на результат:

Before Sorting : [John, Roger, Steven]
After Sorting by ranking : [Steven, John, Roger]

Если нам нужен другой порядок сортировки, нам нужно только изменитьComparator, которые мы используем:

PlayerAgeComparator playerComparator = new PlayerAgeComparator();
Collections.sort(footballTeam, playerComparator);

Теперь, когда мы запускаем нашPlayerAgeSorter, мы видим другой порядок сортировки поage:

Before Sorting : [John, Roger, Steven]
After Sorting by age : [Roger, John, Steven]

4.3. Java 8Comparatorsс

Java 8 предоставляет новые способы определенияComparators с помощью лямбда-выражений и статического фабричного методаcomparing().

Давайте посмотрим на быстрый пример того, как использовать лямбда-выражение для созданияComparator:

Comparator byRanking
 = (Player player1, Player player2) -> player1.getRanking() - player2.getRanking();

МетодComparator.comparing принимает метод, вычисляющий свойство, которое будет использоваться для сравнения элементов, и возвращает соответствующий экземплярComparator:

Comparator byRanking = Comparator
  .comparing(Player::getRanking);
Comparator byAge = Comparator
  .comparing(Player::getAge);

Вы можете подробно изучить функциональность Java 8 в нашем руководствеJava 8 Comparator.comparing.

5. Comparator VsComparable

The Comparable interface is a good choice when used for defining the default ordering или, другими словами, если это основной способ сравнения объектов.

Затем мы должны спросить себя, зачем использоватьComparator, если у нас уже естьComparable?

Есть несколько причин, почему:

  • Иногда мы не можем изменить исходный код класса, объекты которого мы хотим отсортировать, что делает невозможным использованиеComparable

  • ИспользованиеComparators позволяет нам избежать добавления дополнительного кода в наши классы домена

  • Мы можем определить несколько различных стратегий сравнения, что невозможно при использованииComparable

6. Заключение

В этом руководстве мы изучили интерфейсыComparable иComparator и обсудили различия между ними.

Чтобы понять более сложные темы сортировки, ознакомьтесь с другими нашими статьями, такими какJava 8 Comparator,Java 8 Comparison with Lambdas.

И, как обычно, исходный код можно найтиover on Github.