Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сортировка и компараторы

Списки часто нужно упорядочить: имена по алфавиту, товары по цене, сотрудников по отделу и зарплате. Java умеет сортировать сама — нужно лишь объяснить, в каком порядке.

Естественный порядок

Числа и строки уже знают, как сравниваться друг с другом. Это называется естественным порядком. Метод Collections.sort() сортирует список по нему:

void main() {
    List<Integer> numbers = new ArrayList<>(List.of(5, 2, 9, 1));
    Collections.sort(numbers);
    IO.println(numbers);
}

Результат:

[1, 2, 5, 9]

Строки сортируются по кодам символов: заглавные буквы идут раньше строчных.

Компаратор и обратный порядок

Компаратор (Comparator) — объект, который сравнивает два элемента и тем самым задаёт порядок. Метод списка sort() принимает компаратор. Готовый Comparator.reverseOrder() сортирует по убыванию:

void main() {
    List<String> names = new ArrayList<>(List.of("Олег", "Анна", "Ира"));
    names.sort(Comparator.reverseOrder());
    IO.println(names);
}

Результат:

[Олег, Ира, Анна]

Comparator.comparing()

Для своих объектов компаратор удобно строить методом Comparator.comparing(): ему говорят, по какому значению сравнивать. Запись Product::price — ссылка на метод, короткий способ сказать «взять price() у каждого товара»:

record Product(String title, double price) {}
 
void main() {
    List<Product> products = new ArrayList<>(List.of(
            new Product("Чай", 4.5),
            new Product("Кофе", 7.0),
            new Product("Сок", 3.2)));
 
    products.sort(Comparator.comparing(Product::price));
    for (Product p : products) {
        IO.println(p.title() + " " + p.price());
    }
}

Результат:

Сок 3.2
Чай 4.5
Кофе 7.0

Добавьте .reversed() — и порядок станет обратным: Comparator.comparing(Product::price).reversed().

Несколько полей

Если значения первого поля совпали, второе поле задают методом thenComparing(). Здесь сотрудники отсортированы по отделу, а внутри отдела — по имени:

record Employee(String name, String department) {}
 
void main() {
    List<Employee> staff = new ArrayList<>(List.of(
            new Employee("Олег", "склад"),
            new Employee("Анна", "продажи"),
            new Employee("Ира", "склад"),
            new Employee("Борис", "продажи")));
 
    staff.sort(Comparator.comparing(Employee::department).thenComparing(Employee::name));
    staff.forEach(e -> IO.println(e.department() + ": " + e.name()));
}

Результат:

продажи: Анна
продажи: Борис
склад: Ира
склад: Олег

Свой естественный порядок: Comparable

Класс может сам задать свой естественный порядок, реализовав интерфейс Comparable и его метод compareTo(). Метод возвращает отрицательное число, если текущий объект меньше другого, ноль — если равны, и положительное — если больше:

record Version(int major, int minor) implements Comparable<Version> {
    @Override
    public int compareTo(Version other) {
        if (major != other.major) {
            return Integer.compare(major, other.major);
        }
        return Integer.compare(minor, other.minor);
    }
}
 
void main() {
    List<Version> versions = new ArrayList<>(List.of(
            new Version(2, 1), new Version(1, 9), new Version(2, 0)));
    Collections.sort(versions);
    IO.println(versions);
}

Результат:

[Version[major=1, minor=9], Version[major=2, minor=0], Version[major=2, minor=1]]

Частая ошибка

Сортировка меняет сам список. Неизменяемый список из List.of() отсортировать нельзя:

void main() {
    List<Integer> numbers = List.of(3, 1, 2);
    Collections.sort(numbers);
}

Результат:

Exception in thread "main" java.lang.UnsupportedOperationException

Сначала сделайте изменяемую копию: List<Integer> sorted = new ArrayList<>(numbers); — и сортируйте её.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.