Comparator.reversed(): примеры (JAVA)
Comparator.reversed(): ComparatorОписание метода Comparator.reversed()
Метод Comparator.reversed() является дефолтным методом интерфейса Comparator и доступен с Java 8. Подпись метода:
default Comparatorreversed()
Метод не принимает аргументов и возвращает новый Comparator<T>, который налагает обратный порядок по сравнению с исходным компаратором. Возвращаемый компаратор делегирует сравнение оригиналу, но инвертирует результат сравнения: если оригинал возвращает положительное значение, обратный вернёт отрицательное и наоборот. Если оригинальный компаратор возвращает ноль для двух элементов, обратный также вернёт ноль.
Особенности поведения:
- Вызов reversed() на компараторе дважды приводит к эквиваленту исходного компаратора:
c.reversed().reversed()эквивалентенc. - Если исходный компаратор допускает
nullчерез Comparator.nullsFirst или nullsLast, то обратный компаратор сохранит логику обработкиnull, но порядок будет инвертирован в соответствии с общей инверсией. - Метод не бросает проверяемых исключений и сам по себе не возвращает
null. Вызовreversed()наnullневозможен, так как сам объект-компаратор должен существовать. - Если исходный компаратор несогласован с методом
equalsу сравниваемых объектов, обратный компаратор также будет несогласован. Нарушение транзитивности или согласованности с equals может привести к некорректному поведению отсортированных коллекций (TreeSet, TreeMap).
Сериализация: если исходный компаратор реализует Serializable, возвращаемый компаратор может быть сериализуемым, но это не гарантируется в абстрактном описании. На практике многие реализации стандартной библиотеки сохраняют сериализуемость при наличии такой поддержки у исходного компаратора.
Короткие примеры использования
Примеры демонстрируют несколько простых вариантов применения с выводом результата.
Пример 1. Обратный порядок для списка строк (натуральный порядок инвертируется)
List list = Arrays.asList("b", "a", "c");
list.sort(Comparator.naturalOrder().reversed());
System.out.println(list);
[c, b, a]
Пример 2. Использование Comparator.reverseOrder() и reversed() (эквиваленты для натурального порядка)
List nums = Arrays.asList(3, 1, 2);
nums.sort(Comparator.reverseOrder());
System.out.println(nums);
List nums2 = Arrays.asList(3, 1, 2);
nums2.sort(Comparator.naturalOrder().reversed());
System.out.println(nums2);
[3, 2, 1] [3, 2, 1]
Пример 3. Компаратор для объектов по полю и инверсия
class Person { String name; int age; }
List<Person> people = ...;
Comparator<Person> byAge = Comparator.comparingInt(p -> p.age);
people.sort(byAge.reversed());
// Сортировка от старших к младшим
(список Person в порядке убывания возраста)
Пример 4. Обработка null вместе с reversed()
List names = Arrays.asList("bob", null, "alice");
Comparator<String> cmp = Comparator.nullsLast(String::compareTo).reversed();
names.sort(cmp);
System.out.println(names);
[bob, alice, null]
Аналоги и близкие приёмы в Java
- Comparator.reverseOrder() - статический метод, возвращает компаратор, реализующий обратный натуральный порядок. Предпочтителен, когда требуется именно обратный натуральный порядок и не нужен исходный компаратор.
- Collections.reverseOrder() - исторический аналог, возвращает тот же смысл: обратный натуральный порядок или обратный заданному компаратору в перегруженной версии.
- Comparator.comparing(...).reversed() - удобный приём для инверсии компаратора, созданного с помощью фабричных методов сравнения по ключам. Предпочтителен при сравнении по конкретному полю.
- Comparator.nullsFirst / nullsLast - комбинируются с
reversed()для управления расположениемnullпри инверсии.
Выбор зависит от задачи: для обратного натурального порядка можно использовать reverseOrder(), для инверсии пользовательского компаратора - reversed() на этом компараторе, для инверсии только ключевого критерия - построить компаратор через comparing и применить reversed() к нему.
Аналоги в других языках и отличия
Краткие эквиваленты в популярных языках с примерами и результатом.
Python - встроенная поддержка обратного порядка через параметр reverse в sorted и методе списка sort.
a = [3, 1, 2]
print(sorted(a, reverse=True))
[3, 2, 1]
JavaScript - метод sort с инвертированным компаратором.
let a = [3,1,2];
a.sort((x,y) => y - x);
console.log(a);
[3,2,1]
C# - LINQ: OrderByDescending, для массивов класс Array.Sort с Comparer<T>.Create.
var a = new[] {3,1,2};
var r = a.OrderByDescending(x => x).ToArray();
Console.WriteLine(string.Join(",", r));
3,2,1
Kotlin - аналогично Java: sortedWith(compareBy(...).reversed()) или sortedDescending() для натурального порядка.
val a = listOf(3,1,2)
println(a.sortedDescending())
[3, 2, 1]
Go - сортировка с функцией сравнения через sort.Slice, инверсия достигается изменением порядка аргументов.
a := []int{3,1,2}
sort.Slice(a, func(i,j int) bool { return a[i] > a[j] })
fmt.Println(a)
[3 2 1]
PHP - функции rsort для массивов по значению, или usort с инвертированным сравнителем.
$a = [3,1,2];
rsort($a);
print_r($a);
Array ( [0] => 3 [1] => 2 [2] => 1 )
Lua - table.sort с компаратором, инверсия через перестановку параметров.
a = {3,1,2}
table.sort(a, function(x,y) return x > y end)
for i,v in ipairs(a) do print(v) end
3 2 1
SQL - ключевое слово DESC в ORDER BY для обратного порядка.
SELECT name FROM users ORDER BY age DESC;
(записи в порядке убывания возраста)
Отличия от Java: в некоторых языках (Python, Kotlin) обратный порядок является опцией высокого уровня у сортирующих функций, поэтому не требуется создавать отдельный компаратор. В Java чаще строится и комбинируется объект-компаратор, что даёт больше контроля над композициями и поведением при null.
Типичные ошибки при использовании
- Вызов
reversed()наnull-ссылке компаратора. Пример:
Comparator<String> cmp = null;
// cmp.reversed(); // приведёт к NullPointerException
java.lang.NullPointerException
- Ожидание, что
reversed()изменяет исходный компаратор. Метод возвращает новый компаратор, оригинал остаётся без изменений. Пример различия:
Comparator<Integer> c = Comparator.naturalOrder();
Comparator<Integer> r = c.reversed();
List<Integer> a = Arrays.asList(1,2,3);
a.sort(c);
System.out.println(a);
a.sort(r);
System.out.println(a);
[1, 2, 3] [3, 2, 1]
- Нарушение транзитивности или несогласованность с equals при комбинировании компараторов и последующая передача в TreeSet/TreeMap. Результат - непредсказуемое поведение коллекций на основе дерева.
- Неправильная инверсия только части цепочки сравнений. Вызов
(a.thenComparing(b)).reversed()инвертирует весь компаратор целиком. Если требуется инвертировать только первичный критерий, нужно инвертировать именно его:a.reversed().thenComparing(b). Пример неявного сюрприза приведён в расширенных примерах.
Изменения в версии Java
Метод reversed() появился как дефолтный метод в интерфейсе Comparator в Java 8 вместе с другими удобными фабриками и методами композиции (comparing, thenComparing, nullsFirst, nullsLast). В последующих релизах изменений в поведении reversed() не происходило. Развитие API Comparator в основном добавляло дополнительные фабрики и улучшало совместимость, но семантика инверсии осталась прежней.
Расширенные и редкие варианты применения
Несколько нетривиальных кейсов с пояснениями и выводом.
Пример A. Инверсия только первичного ключа при составном сравнении
record Item(String cat, int rank) {}
List<Item> items = List.of(new Item("A",1), new Item("A",2), new Item("B",1));
Comparator<Item> byCat = Comparator.comparing(i -> i.cat);
Comparator<Item> byRank = Comparator.comparingInt(i -> i.rank);
// Требуется: категории по возрастанию, в каждой категории ранги по убыванию
Comparator<Item> cmp = byCat.thenComparing(byRank.reversed());
items.sort(cmp);
System.out.println(items);
[Item[A,2], Item[A,1], Item[B,1]]
Пояснение: инверсия применена только к рангу, что даёт ожидаемую группировку по категории с внутренним убыванием.
Пример B. Инверсия всей цепочки и отличие от инверсии первичного ключа
Comparator<Item> wholeReversed = byCat.thenComparing(byRank).reversed();
List<Item> items2 = new ArrayList<>(items);
items2.sort(wholeReversed);
System.out.println(items2);
[Item[B,1], Item[A,2], Item[A,1]]
Пояснение: здесь сначала инвертируется порядок категорий в целом, затем внутри новой последовательности применяется исходная логика сравнения по рангу. Это не эквивалентно предыдущему примеру.
Пример C. reversed() вместе с nullsFirst/nullsLast
List<String> list = Arrays.asList(null, "a", "b");
Comparator<String> cmp = Comparator.nullsFirst(String::compareTo).reversed();
list.sort(cmp);
System.out.println(list);
[b, a, null]
Пояснение: nullsFirst помещает null в начало в исходном компараторе, но после инверсии null оказывается в конце. Для получения обратного эффекта с сохранением null в начале следует использовать Comparator.nullsLast(...).reversed() в зависимости от желаемого результата.
Пример D. Использование в PriorityQueue и влияния на извлечение
PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.naturalOrder().reversed());
pq.addAll(List.of(1,3,2));
while(!pq.isEmpty()) System.out.print(pq.poll() + " ");
3 2 1
Пояснение: очередь с компаратором в обратном порядке выдаёт элементы от наибольших к наименьшим.
Пример E. Сериализация компаратора и осторожность
Comparator<String> base = Comparator.comparing(String::length);
Comparator<String> rev = base.reversed();
// Если 'base' сериализуем, 'rev' зачастую тоже; проверка возможна через instanceof Serializable
System.out.println(base instanceof java.io.Serializable);
System.out.println(rev instanceof java.io.Serializable);
false false
Пояснение: у стандартных компараторов сериализуемость зависит от конкретной реализации и от того, предоставлялся ли сериализуемый лямбда-выражением или методом. В критичных случаях сериализуемость следует проверять отдельно.
Пример F. Взаимодействие с TreeSet и последствия несогласованности
Comparator<String> broken = (a,b) -> a.length() - b.length();
TreeSet<String> set = new TreeSet<>(broken.reversed());
set.addAll(List.of("a","bb","ccc"));
System.out.println(set);
[ccc, bb, a]
Пояснение: если компаратор сравнивает только длину строк, элементы с одинаковой длиной считаются равными для множества, и одно из значений может быть отброшено. Инверсия не исправляет саму проблему; при необходимости следует комбинировать с дополнительным критерием через thenComparing.
Пример G. Комбинация reversed() с thenComparing, чтобы задать детерминированный порядок
Comparator<String> comp = Comparator.comparingInt(String::length)
.reversed()
.thenComparing(Comparator.naturalOrder());
List<String> l = List.of("bb","a","aa");
List<String> l2 = new ArrayList<>(l);
l2.sort(comp);
System.out.println(l2);
[aa, bb, a]
Пояснение: для элементов одинаковой длины добавлен вторичный критерий, что даёт стабильный и предсказуемый порядок.
джава Comparator.reversed() function comments
- джава Comparator.reversed() - аргументы и возвращаемое значение
- Функция java Comparator.reversed() - описание
- Comparator.reversed() - примеры
- Comparator.reversed() - похожие методы на java
- Comparator.reversed() на javascript, c#, python, php
- Comparator.reversed() изменения java
- Примеры Comparator.reversed() на джава