Сортировка ArrayList объектов Java по полю: полный гайд с кодом

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как приручить хаос: сортируем ArrayList объектов по любому полю в Java

Представьте: вы пишете приложение для каталога книг. У вас есть класс Book с полями title, author, price и year. Вы складываете десятки книг в ArrayList. И тут пользователь просит: «Покажи сначала самые новые!», а через минуту — «Нет, отсортируй по цене!».

Если вы хоть раз сталкивались с такой задачей, то знаете — просто вызвать Collections.sort(list) не получится. Java не умеет читать мысли. Ей нужно объяснить: по какому именно полю сортировать. Именно об этом — самый популярный вопрос на Stack Overflow по теме сортировки кастомных объектов.

В этой статье мы разберём все способы сортировки ArrayList по свойству объекта: от классического Comparable до элегантных лямбда-выражений и Stream API. Поехали!

Подготовка: класс, который будем сортировать

Для примеров создадим простой класс Person:

public class Person {
    private String name;
    private int age;
    private double salary;

    public Person(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    // Геттеры (сеттеры опустим для краткости)
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getSalary() { return salary; }

    @Override
    public String toString() {
        return name + " (" + age + " лет, $" + salary + ")";
    }
}

И наполним наш список:

ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Анна", 28, 55000));
people.add(new Person("Борис", 35, 72000));
people.add(new Person("Виктор", 22, 48000));
people.add(new Person("Галина", 30, 62000));

Теперь всё готово к экспериментам. Как отсортировать этот список по возрасту? По имени? По зарплате? У Java есть минимум три способа. Начнём с фундаментального.

Способ 1: Comparable — встраиваем логику сортировки прямо в класс

Интерфейс Comparable — это «естественный порядок» объекта. Если вы хотите, чтобы ваши объекты всегда сортировались по одному полю (например, по возрасту), реализуйте этот интерфейс.

Изменим наш класс Person:

public class Person implements Comparable<Person> {
    // ... поля, конструктор, геттеры ...

    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }
}

Теперь сортировка — одна строка:

Collections.sort(people);
System.out.println(people);
// Вывод: [Виктор (22 лет, $48000), Анна (28 лет, $55000), Галина (30 лет, $62000), Борис (35 лет, $72000)]

Плюсы: простота, не нужно думать о внешних компараторах.
Минусы: вы «зашиваете» в класс только один способ сортировки. Если завтра понадобится сортировать по имени — придётся менять класс.

Кстати, для обратного порядка (от большего к меньшему) можно использовать Collections.reverseOrder():

Collections.sort(people, Collections.reverseOrder());

Способ 2: Comparator — гибкость без изменения класса

Когда нужно сортировать по-разному в разных местах программы, на помощь приходит Comparator. Это отдельный класс или анонимный класс, который содержит логику сравнения.

Анонимный класс — дедушкин метод

Collections.sort(people, new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
});

Работает, но выглядит громоздко. В современной Java так не пишут.

Лямбда-выражения — лаконичность и мощь

Java 8 принесла лямбды. Сравнение по имени умещается в одну строку:

people.sort((p1, p2) -> p1.getName().compareTo(p2.getName()));

А если нужно по убыванию? Легко:

people.sort((p1, p2) -> p2.getAge() - p1.getAge());

Но будьте осторожны с вычитанием для чисел с плавающей точкой! Для salary используйте Double.compare:

people.sort((p1, p2) -> Double.compare(p1.getSalary(), p2.getSalary()));

Статические методы Comparator — ещё удобнее

Самый элегантный способ — использовать геттеры и ссылки на методы:

// По возрасту (по возрастанию)
people.sort(Comparator.comparingInt(Person::getAge));

// По имени (по алфавиту)
people.sort(Comparator.comparing(Person::getName));

// По зарплате (по убыванию)
people.sort(Comparator.comparingDouble(Person::getSalary).reversed());

Обратите внимание: comparingInt и comparingDouble — специализированные версии для примитивов, они работают быстрее.

Сортировка по нескольким полям

Хотите отсортировать сначала по возрасту, а если возраст одинаковый — по имени? thenComparing к вашим услугам:

people.sort(Comparator.comparingInt(Person::getAge)
                     .thenComparing(Person::getName));

Можно добавлять сколько угодно полей:

people.sort(Comparator.comparing(Person::getName)
                     .thenComparingInt(Person::getAge)
                     .thenComparingDouble(Person::getSalary).reversed());

Способ 3: Stream API — сортировка без изменения исходного списка

Иногда не нужно менять оригинальный список, а достаточно получить отсортированный поток данных. Тут в игру вступает Stream.sorted():

List<Person> sortedByAge = people.stream()
        .sorted(Comparator.comparingInt(Person::getAge))
        .collect(Collectors.toList());

Оригинальный список people остался нетронутым. Очень полезно, когда исходные данные нужны в первоначальном порядке.

Можно комбинировать с другими операциями:

people.stream()
      .filter(p -> p.getAge() > 25)
      .sorted(Comparator.comparing(Person::getName))
      .forEach(System.out::println);

Это выведет только людей старше 25 лет, отсортированных по имени, не изменяя исходный список.

Какой способ выбрать? Сравнение и рекомендации

Давайте подведём итог и выберем инструмент под задачу:

  • Comparable — используйте, если у объекта есть один «естественный» порядок (например, дата для событий, ID для записей в

Блоги

Рекомендаций по книгам