Comparator Java
Comparable<T> задаёт естественный порядок внутри самого типа.
Comparator<T> задаёт внешний порядок и позволяет иметь несколько вариантов
сортировки одних объектов.
record Candidate(String name, int score) {}
Comparator<Candidate> ranking =
Comparator.comparingInt(Candidate::score)
.reversed()
.thenComparing(Candidate::name);
List<Candidate> ordered = candidates.stream()
.sorted(ranking)
.toList();Результат сравнения
compare(a, b) или a.compareTo(b) возвращает:
- отрицательное число, если
a < b; - ноль, если элементы равны по порядку;
- положительное число, если
a > b.
Важен знак, а не конкретные -1 и 1. Нельзя сравнивать return a - b:
разность может переполниться. Используются Integer.compare(a, b),
Long.compare и фабрики Comparator.
Контракт порядка
Знак сравнения должен быть антисимметричным и транзитивным. Если
compare(a, b) == 0, компаратор считает элементы эквивалентными по порядку.
Желательно, чтобы это согласовывалось с equals, но API не требует этого
всегда.
Особенно важно для TreeSet и TreeMap: элементы с результатом 0
считаются одним ключом, даже если equals возвращает false.
Полезные композиции
| Задача | Метод |
|---|---|
| поле объекта | comparing |
| примитивное int-поле | comparingInt |
| второй критерий | thenComparing |
| обратный порядок | reversed |
| null в начале/конце | nullsFirst, nullsLast |
| естественный порядок | naturalOrder |
Comparator.nullsLast(Comparator.naturalOrder()) явно задаёт поведение для
null; большинство обычных компараторов иначе бросит NullPointerException.
Связанные темы: Коллекции, Stream API, Equals и hashCode.
Вопросы для собеседования
- Comparable или Comparator? Comparable — один естественный порядок в классе, Comparator — внешние и альтернативные порядки.
- Почему нельзя возвращать
a - b? Из-за переполнения. - Что значит результат 0 в TreeSet? Элемент эквивалентен существующему и обычно не будет добавлен.
- Обязан ли compare быть согласован с equals? Не всегда, но несогласованность меняет поведение sorted set/map и должна быть осознанной.
- Как сравнивать по нескольким полям? Через
thenComparing. - Как обработать null? Явно обернуть comparator в
nullsFirst/nullsLast.