Продолжим тему прошлого поста про сортировки и функциональный подход. Итак, компаратор задаёт правило сравнения элементов между собой. Делает он это с помощью метода compare:
public int compare(T o1, T o2) {…}Если метод вернул
▫️ число больше нуля — первый элемент больше второго
▫️ 0 — элементы равны
▫️ число меньше нуля — первый меньше второго
Простейшая и популярная реализация — вычесть одно значение из другого:
(o1, o2) -> (int) (o1.getSum() - o2.getSum())❓ Что с этим не так?
Я всегда сомневаюсь, что из чего вычитать. Если вы отвечали на опрос дольше одной секунды, значит мы в одном лагере:) Компаратор — совсем не то место, где мозг должен спотыкаться.
В Java 8 в интерфейсе Comparator появился удобный метод:
orders.sort(comparing(Order::getSum))Что классно:
✅ Не надо вспоминать, что из чего вычитать
✅ Легко сделать сравнение в обратном порядке:
comparing(Order::getSum).reversed()✅ Можно учесть null:
nullsFirst(comparing(Order::getSum))
nullLast(…)✅ Удобно сортировать по нескольким полям:
comparing(Order::getSum).thenComparing(Order::getId)Самостоятельно обрабатывать null и писать сложные сортировки очень утомительно. Помню, как с удовольствием удаляла из проекта компараторы на 20 строк после перехода на Java 8😊
Важные нюансы:
1️⃣ comparing*
В интерфейсе Comparator также доступны методы comparingInt, comparingLong и comparingDouble. Используются для полей примитивного типа, чтобы избежать лишнего боксинга. Если в классе Order
Long id → используем comparing(Order::getId)
long id → comparingLong(Order::getId)
Не указывайте тип лишний раз. Для работы с объектами подойдёт обычный comparing
2️⃣ Нетривиальная работа с null*
В обычных методах легко понять, что происходит:
comparing(A).reversed().thenComparing(Б)
=
отсортировать по полю А в обратном порядке, дубликаты отсортировать по Б
Методы null* выбиваются из этой схемы.
nullsFirst(comparing(Order::getSum))означает, что первыми будут null объекты, а существующие заказы отсортируются по сумме. Этот компаратор работает для такого кода:
orders.add(null); // эти элементы будут впередиorders.add(new Order(…)); // эти отсортируются по полю sum Если в списке нет null объектов, но в поле sum возможен null, придётся писать так:
…comparing(Order::getSum, nullsFirst(naturalOrder()));Сравнение по нескольким nullable полям выглядит совсем плохо. К счастью, на практике такие задачи встречаются редко.
Ответ на вопрос перед постом:
(o1, o2) -> (int) (o2.getSum() - o1.getSum())Но лучше использовать
comparing(Order::getSum).reversed() ✨