Comparable и Comparator

Базовый Java
≈ 2 мин чтения

Когда объектам нужен порядок

Для чисел и строк порядок уже понятен, но для пользовательских объектов Java не может угадать, что считать «меньше» или «больше». Один и тот же объект можно сравнивать по имени, цене, дате или любому другому признаку. Для описания порядка используются Comparable и Comparator.

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

Если у класса есть один основной, естественный способ упорядочивания, он может реализовать Comparable<T> и метод compareTo().

class Level implements Comparable<Level> {
    private final int value;

    Level(int value) {
        this.value = value;
    }

    @Override
    public int compareTo(Level other) {
        return Integer.compare(value, other.value);
    }
}

Результат сравнения меньше нуля означает «этот объект раньше», ноль — одинаковую позицию в порядке, а значение больше нуля — «этот объект позже». Не нужно возвращать именно -1, 0 или 1: важен знак результата.

Comparator: отдельное правило сравнения

Когда способов сортировки несколько или класс менять нельзя, правило выносится в Comparator<T>.

class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String left, String right) {
        return Integer.compare(left.length(), right.length());
    }
}

List<String> names = new ArrayList<>(List.of("Bob", "Alexander", "Ann"));
names.sort(new LengthComparator());

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

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

Для типов, которые уже реализуют Comparable, стандартная библиотека предоставляет готовые компараторы.

List<Integer> numbers = new ArrayList<>(List.of(3, 1, 2));

numbers.sort(Comparator.naturalOrder());
numbers.sort(Comparator.reverseOrder());
Ноль в сравнении означает «равны с точки зрения порядка». Если естественный порядок класса противоречит его equals(), некоторые структуры и алгоритмы могут вести себя неожиданно. Такое расхождение должно быть осознанным.