undefined

Comparator, сортировка коллекций

Java Multithreading
6 уровень , 1 лекция
Открыта
Comparator, сортировка коллекций - 1

— Привет, Амиго!

— Привет, Билаабо!

— Сегодня будет небольшая, но интересная и полезная тема – сортировки коллекций.

— Сортировка? Я что-то про это слышал.

— Давным-давно каждый программист обязан был уметь писать сортировку. Умел и писал. Но те времена канули в лету. Сегодня написание своей сортировки считается дурным тоном, как и написание всего, что уже было придумано.

В Java (да и других языках программирования) сортировки уже реализованы. Твоя задача – научиться правильно пользоваться тем, что есть.

— Ок.

— У вспомогательного класса Collections есть статический метод sort, который используется для сортировки коллекций, а если точнее – списков. Элементы в коллекциях Map и Set не имеют порядка/номера, значит, и сортировать там нечего.

— Да, я вспомнил, я когда-то уже использовал этот метод для сортировки списка чисел.

— Отлично. Но этот метод гораздо мощнее чем, кажется на первый взгляд. Он может сортировать не только числа, но и любые объекты, по любым критериям. И помогают ему в этом два интерфейса: Comparable и Comparator.

Иногда бывает нужно отсортировать объекты, а не числа. Например, у тебя есть список людей, и ты хочешь отсортировать их по возрасту. Для этого есть интерфейс Comparable.

Давай я сначала покажу тебе пример, и все станет понятнее:

Пример
public class Woman implements Comparable<Woman>
{
public int age;

public Woman(int age) {
this.age = age;
}

public int compareTo(Woman o)
{
return this.age - o.age;
}
}
Пример использования:
public static void main(String[] args )
{
ArrayList<Woman> women = new ArrayList<Woman>();
women.add(new Woman(18));
women.add(new Woman(21));
women.add(new Woman(5));

Collections.sort(women);
}

Чтобы объекты можно было сортировать, сначала нужно научиться их сравнивать. Для этого и используется Comparable. Интерфейс Comparable является generic’ом – т.е. типом с параметром. У него всего один generic-метод – compareTo(T o). В этом методе и происходит сравнение переданного объекта (o) и текущего (this). Т.е. надо переопределить этот метод в своем классе и сравнить в нем текущий объект (this) с переданным.

— А как работает compareTo? Я думал, что он будет возвращать true/false в зависимости от того – больше переданный объект или меньше.

— Тут все немного хитрее. Метод compareTo возвращает не true/false, а значение типа int. На самом деле так сделано для простоты.

Когда компьютеру нужно определить больше ли одно число, чем другое, он просто вычитает из первого числа второе, а потом смотрит, что получилось. Если 0 – числа равны, если получилось число меньше нуля, то второе число больше, а если результат больше нуля, то больше уже первое число.

Тут используется та же логика. Согласно спецификации метод compareTo должен вернуть ноль, если сравниваемые объекты равны. Если метод compareTo вернул число больше нуля, это значит, что наш (this) объект больше, чем переданный. Если метод compareTo вернул число меньше нуля, то объект this меньше чем переданный.

— Немного странно.

— Да, но если ты сравниваешь объекты просто по какому-то параметру-числу, то можешь просто вернуть разницу между ними – вычесть один из другого. Как это и сделано в примере выше.

public int compareTo(Woman o)
{
return this.age - o.age;
}

— Вроде все понятно. Хотя может и не все. Но почти все.

— Отлично. Теперь рассмотрим более практическую задачу. Ты написал крутой сайт по пошиву женской одежды в Китае. Для описания своих пользователей ты используешь класс Woman. Ты даже сделал страницу с таблицей, где можешь посмотреть их всех. Но есть проблема…

Объект Woman содержит у тебя не только возраст, а еще целую кучу данных: имя, фамилию, рост, вес, количество детей, …

В таблице пользователей есть много колонок, и тут встает вопрос: а как сортировать пользователей по разным критериям? По весу, по возрасту, по фамилии?

— Гм. Действительно, часто вижу таблицы с сортировкой колонок. И как это сделать?

— А для этого есть второй интерфейс, о котором я хотел тебе сегодня рассказать – это интерфейс Comparator. И у него тоже есть метод сравнения, только он называется compare и принимает не один параметр, а два: int compare(T o1, T o2). Вот как это работает:

Пример
public class Woman {

public int age;
public int childrenCount;
public int weight;
public int height;
public String name;

public Woman(int age, int childrenCount, int weight, int height, String name) {

    this.age = age;
    this.childrenCount = childrenCount;
    this.weight = weight;
    this.height = height;
    this.name = name;
    }
}
Пример использования:
public static void main(String[] args ) {

    ArrayList<Woman> women = new ArrayList<Woman>();
    women.add(new Woman(18, 0, 45, 170, "Ann"));
    women.add(new Woman(21, 1, 57, 168, "Iren"));
    women.add(new Woman(5, 0, 20, 110, "Angelina"));
    …

    Comparator<Woman> compareByHeight = new Comparator<Woman>() {

    public int compare(Woman o1, Woman o2) {
    return o1.height - o2.height;
    }
    };

    Collections.sort(women, compareByHeight);
}

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

— Т.е. я могу сделать несколько классов, реализующих интерфейс Comparator, но в каждом из них сравнивать разные параметры? В одном – weight, в другом – age, в третьем – height?

— Да, это очень просто и удобно.

Мы просто вызываем метод Collections.sort, передаем туда список объектов и еще специальный объект во втором параметре, который реализует интерфейс Comparator и говорит, как правильно сравнивать пары объектов в процессе сортировки.

— Гм. Вроде все понятно. Дай-ка я сам попробую. Допустим, мне нужно отсортировать пользователей по весу, это будет так:

Пример кода, пользователи сортируются по весу:
Comparator<Woman> compareByWeight = new Comparator<Woman>() {

public int compare(Woman o1, Woman o2) {
 
    return o1.weight - o2.weight;
    }
};

Collections.sort(women, compareByWeight);

— Да, именно так.

— Отлично. А если я хочу отсортировать в обратном порядке?

— А подумать? Ответ очень простой!

— Придумал! Вот так:

Сортировка по возрастанию:
return o1.weight - o2.weight;
Сортировка по убыванию:
return o2.weight – o1.weight;

— Правильно. Молодец.

— А если я хочу сортировать по фамилии? Как сортировать строки, Билаабо?

— А у строк уже реализован метод compareTo, надо просто вызвать его:

Пример кода, пользователи сортируются по имени:
Comparator<Woman> compareByName = new Comparator<Woman>() {

public int compare(Woman o1, Woman o2) {

    return o1.name.compareTo(o2.name);
}
};

Collections.sort(women, compareByName);

— Это был отличный урок, Билаабо, спасибо тебе большое.

— И тебе спасибо, друг!

Комментарии (57)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
canny 27 уровень
30 апреля 2021
В первом же примере : public int compareTo(Woman o) { return this.age - o.age; } Отличный критерий для сортировки сущности Woman 🤣 (age)
ЕП 29 уровень, Санкт-Петербург
5 апреля 2021
Всё, многопоточности уже хватит?
Vitalachka 35 уровень, Лондон
4 марта 2021
Pavel Kurchavov 32 уровень, Тверь
18 февраля 2021

women.sort((a, b) -> { /*something code*/ });
Не благодарите
Andrei Po 32 уровень
13 января 2021
полезный комментарий 2х летней давности, эффективный способ сортировки в обратном порядке, когда есть готовый givenCompаrator

Collections.sort(myCollection, givenCompаrator.reversed());
Maxim Frolov 27 уровень, Нижний Новгород
15 июня 2020
...сегодня написание своей сортировки считается дурным тоном, как и написание всего, что уже было придумано...прямо мысль дня для меня...
Kex 38 уровень, Тольятти Expert
21 мая 2020
А где мультипоточность? у меня с ней ваще все сложно.
Kubik_13 34 уровень, Москва
7 апреля 2020

public int compareTo(Woman o)
{
return this.age - o.age;
}
а если возвращается ноль -> получается обьекты равны, но какой то из них должен стоять раньше в списке. какой поставится раньше?
Игорь Кучер 38 уровень, Киев Expert
7 февраля 2020
Как раз вчера разобрался с этими двумя интерфейсами :D
BrokenDreams 33 уровень, Москва
5 февраля 2020
Я так понимаю, что в квесте Collections мы будем проходить Multithreading?