Автор
Владимир Портянко
Java-разработчик в Playtika

Метод compareTo

Статья из группы Java Developer
участников
Для упорядочивания объектов одного типа, хранящихся в массиве или коллекции, разработчики Java придумали интерфейс Comparable. В нём объявлен всего один метод, compareTo:
public interface Comparable<T> {
    public int compareTo(T o);
}
Метод compareTo - 1Интерфейс Comparable параметризирован типом объекта, который он принимает в качестве параметра в метод compareTo. В данном случае мы предупреждаем компилятор, какие типы объектов собираемся сравнивать. Если условие идентичности типов не будет выполняться, то мы получим ошибку ClassCastException. Метод compareTo в Java сравнивает вызывающий объект с объектом, переданным в качестве параметра, и возвращает в результате выполнения сравнения целое число:
  • положительное, если вызывающий объект больше объекта, переданного в качестве параметра;
  • отрицательное, если вызывающий объект меньше объекта, переданного в качестве параметра;
  • нуль, если объекты равны.
Написание логики сравнения объектов – забота исключительно разработчика класса и определяется она желаемыми результатами при упорядочивании.

Зачем нужен метод compareTo в Java?

Программисту на Java очень часто приходиться иметь дело с массивами и списками объектов. При работе с большим количеством данных их зачастую удобно хранить в упорядоченном или отсортированном виде. Во-первых, это ускоряет работу с коллекцией при поиске нужной информации, во-вторых — упорядоченные данные визуально лучше воспринимаются.
Метод compareTo - 2
Одним из самых простых и эффективных способов отсортировать массив объектов является метод sort() класса Arrays, а коллекцию объектов в виде списка – аналогичный метод класса Collections. Для сортировки с помощью этих методов разработчики Java предоставили нам свободу в выборе способа задания критериев сортировки: с реализацией интерфейса Comparable в классе объектов, которые мы хотим упорядочить, или с использованием интерфейса Comparator. В первом случае методы сортировки принимают набор объектов в виде массива или списка:
sort(T[]array)//сортировка массива
sort(List<T> list)// сортировка списка
а во втором – плюс еще реализацию интерфейса Comparator:
sort(T[]array, Comparator <? super T> comparator)//сортировка массива
sort(List<T> list, Comparator <? super T> comparator)// сортировка списка
Интерфейс Comparable используется, когда мы хотим задать естественный (наиболее логичный с нашей точки зрения) порядок расположения объектов при сортировке. Он также является способом «зашить» алгоритм сравнения объектов этого класса на стадии его проектирования. Например, с помощью реализации этого интерфейса, определены критерии естественного упорядочивания в классах-обертках основных примитивных типов: Byte, Character, Long, Integer, Short, Double, Float, Boolean, String. Это также означает, что в этих классах есть реализованный метод compareTo, который при необходимости мы можем использовать в программе. Давайте посмотрим на примере сравнения строк, как реализован этот метод в классе String.
String str1="Аарон";
        String str2="АAPOH";
        String str3="аарон";
        String str4="ААрон";
        String str5="аАрон";
        String str6="Берта";
        String str7="берта";
String[] allStr=new String[]{str1,str2,str3,str4, str5,str6, str7};
        Arrays.sort(allStr);
        for (String s:allStr){
            System.out.println(s);
        }
    }
Если этот код выполнить в методе main, то получим такой результат:
АAPOH
ААрон
Аарон
Берта
аАрон
аарон
берта
Как видно из примера в классе String, метод compareTo упорядочивает строки в алфавитном порядке, лексикографически и с учетом регистра. Именно такой порядок сравнения строк определен разработчиками класса String как естественный. Для более простого понимания, что такое лексикографический порядок, достаточно вспомнить, как расположены слова в языковых словарях. При сравнении чисел объекты упорядочиваются в порядке возрастания. Такая логика сравнения заложена в классах Byte, Character, Long, Integer, Shor, Double, Float.

Реализуем сравнение в своем классе

Посмотрим на примере как можно встроить возможность сравнения объектов в свой класс. При реализации метода compareto Java мы можем задать один или несколько критериев упорядочивания объектов, а также задействовать методы compareto из классов String и Integer. Например, для объектов класса User мы задаем сортировку по имени, а в случае равенства имен – по возрасту. Объекты будут располагаться в естественном порядке (по мере увеличения значения). Класс User:
public class User  implements Comparable <User>{//добавляем возможность сравнивать объекты User

    private String name;
    private Integer age;
    private String email;

    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    @Override
//реализуем метод compareTo интерфейса Comparable
    public int compareTo(User o) {

//используем метод compareTo из класса String для сравнения имен
        int result = this.name.compareTo(o.name);

//если имена одинаковые -  сравниваем возраст,
используя метод compareTo из класса Integer

        if (result == 0) {
            result = this.age.compareTo(o.age);
        }
        return result;
    }

    @Override
    public String toString() {
        return "{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }
Протестируем работу метода compareTo, реализованного в классе User, c помощью метода sort класса Collections:
public static void main(String[] args) {
    User user = new User("Андрей", 19, "andryha@mail.ru");
    User user2 = new User("Олег", 25, "oleg@mail.ru");
    User user3 = new User("Андрей", 24,"opr@google.com");
    User user4 = new User("Игорь", 16, "igor@mail.ru");
    User user5 = new User("Андрей", 44,"stary@google.com");
    List<User> list = new ArrayList<>();

    list.add(user);
    list.add(user2);
    list.add(user3);
    list.add(user4);
    list.add(user5);

    System.out.println("-------до сортировки--------");
    for (User u : list) {
        System.out.println(u);
    }
    System.out.println("-------после сортировки-----");
    Collections.sort(list);
    for (User u : list) {
        System.out.println(u);
    }
}
 }
}
Результат работы метода main:
-------до сортировки--------
{name='Андрей',  age=19,  email='andryha@mail.ru'}
{name='Олег',    age=25,  email='oleg@mail.ru'}
{name='Андрей',  age=24,  email='opr@google.com'}
{name='Игорь',   age=16,  email='igor@mail.ru'}
{name='Андрей',  age=44,  email='stary@google.com'}
-------после сортировки-----
{name='Андрей',  age=19,  email='andryha@mail.ru'}
{name='Андрей',  age=24,  email='opr@google.com'}
{name='Андрей',  age=44,  email='stary@google.com'}
{name='Игорь',   age=16,  email='igor@mail.ru'}
{name='Олег',    age=25,  email='oleg@mail.ru'}
Итак, подведем итог. Если вы — сторонник порядка во всем и хотите без лишнего кода расположить ваши объекты в массиве или списке – используете интерфейс Comparable. Реализация его метода compareTo позволяет достаточно легко встроить механизм естественного упорядочивания объектов вашего класса. Если вам приходится работать с коллекциями и массивами объектов стандартных классов, описанных в библиотеке Java, используйте уже готовые реализации compareTo в этих классах.
Комментарии (22)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Денис
Уровень 23
17 февраля 2022, 08:27
Нужно пояснение.
public int compareTo(User o) {
        int result = this.name.compareTo(o.name);

//если имена одинаковые -  сравниваем возраст,
используя метод compareTo из класса Integer

        if (result == 0) {
            result = this.age.compareTo(o.age);
//сравнили возраст, получили какой-то результат
        }
        return result;
//вернули результат в compareTo, а дальше то что?
    }
Что происходит дальше после возвращения результата в compareTo, какой дальнейший алгоритм сортировки?
Dregid Backend Developer в Сбербанк
1 сентября 2022, 12:50
Он возвращает число, это число использует метод sort() для того чтобы рассортировать объекты в списке. То есть, к примеру, есть имя начавшееся на букву А, она вернет наименьшее число в отличии от имени начавшаяся на букву Б. Соответственно самое наименьшее число, получает первый индекс в списке и т.д. P.S. Знаю что скорее тебе это уже не нужно, но другим поможет P.S.S. Я рассказываю от того как понял сам эту статью. Поэтому не нужно, если я не прав кидаться картошкой) Просто поправьте
Wiun
Уровень 16
30 марта 2021, 08:32
не совсем понимаю, что такое
//используем метод compareTo из класса String для сравнения имен
        int result = this.name.compareTo(o.name);
мы сравниваем переменную name из класса с какой переменной name? какое имя будет подставляться в аргумент "о"? и дальше. мы метод compareTo нигде не вызываем, а пользуемся Collections.sort так зачем мы его описывали и переопределяли?
AlexS
Уровень 19
1 апреля 2021, 21:49
мы сравниваем переменную name из класса с какой переменной name? какое имя будет подставляться в аргумент "о"? - с переменной name но уже другого объекта User. Если эти поля равны, то выполняем сравнение возраста. и дальше. мы метод compareTo нигде не вызываем, а пользуемся Collections.sort так зачем мы его описывали и переопределяли? compareTo вызывается всегда когда к нему обращаемся. Или в процессе вывода выполняем сортировку коллекции по нашим правилам, заданным в compareTo. Collections.sort(list); Переопределяли для того, что бы сравнить наших user по именам, и возрасту. Т.е мы собрали из стандартных реализаций compareTo для String, Integer переопределенный compareTo для нашей задачи.
Wiun
Уровень 16
2 апреля 2021, 11:54
как-то еще сложно) спасибо, будем вдумываться)
AlexS
Уровень 19
4 апреля 2021, 12:31
осознание придет,) - Главное задавайте больше вопросов и прогресс будет быстрее
roller
Уровень 23
14 апреля, 06:57
Как я понимаю: public int compareTo() { int result = this.name(поле name объекта у которого мы будем вызывать метод).compareTo(o.name); <--- а это уже объект с которым мы сравниваем. Пример: У объекта User andrey поле name = "Андрей", а у объекта User andrey1 поле name = "Андроид". andrey.compareTo(andrey1); Следовательно результат будет отрицательным числом, т.к поля name у этих объектов разные. P.S Если что, поправьте пожалуйста :)
Viktor Korotkov
Уровень 23
22 декабря 2020, 15:11
Не, ну почему если Андрюхе 44 - он сразу старый?😭
Артем Стукалов
Уровень 23
Expert
5 июля 2020, 17:35
Господи, почему же в гугле нормально всё объясняют с короткими и простыми примерами, а тут через Жопу ?
Daganet
Уровень 22
17 апреля 2020, 22:34
Поясните пж, почему не сортирует по возрасту? получается результат работы метода в этой статье не рабочий.
22 мая 2020, 22:51
все работает. У тебя ошибка. Перечитай статью еще раз. Если не найдешь - пиши в тг @AgentMikle007 за 300 руб покажу где у тебя ошибка)
Миша
Уровень 16
29 мая 2020, 07:35
Вот что карантин с людьми делает
Kirill Badin
Уровень 27
28 июня 2020, 16:28
Результат переопределить забыли, если ещё актуально)
Denis Sidorenko Java Developer в СберТех
16 марта 2020, 06:21
Например, с помощью реализации этого интерфейса, определены критерии естественного упорядочивания в классах-обертках основных примитивных типов: Byte, Character, Long, Integer, Short, Double, Float, Boolean, String. с каких пор String стал относится к примитивным типам?
Marie Backend Developer в freelance
24 мая 2020, 15:40
> в классах-обёртках
Roman Asanov
Уровень 0
27 мая 2020, 18:56
А String обертка для какого примитива?)
Marie Backend Developer в freelance
29 мая 2020, 16:14
Если уж так хочется докопаться, то в классах-обертках и String
GrinTea
Уровень 16
26 февраля 2020, 17:46
Как видно из примера в классе String, метод compareTo упорядочивает строки в алфавитном порядке, лексикографически и с учетом регистра. Я так понимаю что в статье опечатка. Написано метод compareTo, а должен быть повидимому .sort
Игорь Birt
Уровень 24
Expert
16 апреля 2020, 13:11
Нет, все верно, метод sort работает так, как ему велит Comparable
Арсен
Уровень 22
11 февраля 2020, 15:42
приходится. а не приходиться. ПрихОдится работать и приходИться кому-либо братом/сватом.
Владимир
Уровень 0
2 ноября 2018, 17:32
Объясните, пожалуйста. Получается, что при использовании Collections.sort(). Метод compareTo выполняется автоматически? Мы ведь его не вызываем.
Dinara
Уровень 25
1 декабря 2018, 22:25
Да, получается так. Вот тут описание метода Collections.sort() Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. Примерный перевод: Сортирует указанный список по возрастанию, согласно естественному порядку расположения его элементов. А реализация интерфейса Comparable как раз задает natural ordering, или естественный порядок расположения, как описано тут (This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering. - Этот интерфейс задает порядок расположения объектов каждого класса, который его реализует. Этот порядок называется естественным порядком расположения.)