Stream.sorted: примеры (JAVA)
Stream.sorted: StreamОписание метода Stream.sorted
Метод Stream.sorted в Java предоставляет способ упорядочивания элементов потока. Это промежуточная, состоящая операцией, которая принимает элементы, буферизует их, выполняет сортировку и возвращает новый поток с установленным порядком элементов.
Сигнатуры и варианты:
Stream<T> sorted()- сортировка по естественному порядку. Элементы должны реализовыватьComparable<T>. В противном случае при попытке сравнения возникнетClassCastException.Stream<T> sorted(Comparator<? super T> comparator)- сортировка с заданным компаратором. Если в качестве аргумента переданnull, будетNullPointerException.- Примитивные специализированные потоки:
IntStream.sorted(),LongStream.sorted(),DoubleStream.sorted()- сортировка примитивных значений по возрастанию.
Поведение и характеристики:
- Операция является stateful (сохраняет весь вход для сортировки) и «между промежуточными»: сортировка не выполняется лениво для каждого элемента, а только при применении терминальной операции.
- Для упорядоченных потоков сортировка стабильна - равные элементы сохраняют порядок встречи.
- Сложность временная примерно O(n log n), требуется дополнительная память O(n) для буферизации всех элементов.
- В параллельных потоках сортировка выполняется с использованием параллельных алгоритмов, что повышает требования к памяти и синхронизации. Компараторы должны быть потокобезопасны, если они используют внешнее состояние.
- Возвращаемое значение: новый
Stream<T>(или соответствующий примитивный поток) с отсортированными элементами; исходный источник не изменяется.
Когда используется
Когда требуется получить элементы в определенном порядке перед дальнейшей обработкой или коллекционированием - например, при вывода топ-N, агрегировании по упорядоченным критериям, или при представлении данных пользователю.
Короткие примеры использования
1) Сортировка по естественному порядку строк
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of("b", "a", "c")
.sorted()
.forEach(System.out::print);
}
}
abc
2) Сортировка с компаратором - обратный порядок
import java.util.stream.Stream;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Stream.of(1, 3, 2)
.sorted(Comparator.reverseOrder())
.forEach(System.out::print);
}
}
321
3) Сортировка примитивного потока
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.of(5, 2, 9)
.sorted()
.forEach(System.out::print);
}
}
259
4) Сортировка объектов по полю
import java.util.stream.Stream;
import java.util.Comparator;
record Person(String name, int age) {}
public class Main {
public static void main(String[] args) {
Stream.of(new Person("Ann", 30), new Person("Bob", 25))
.sorted(Comparator.comparing(Person::age))
.forEach(p -> System.out.print(p.name()));
}
}
BobAnn
Похожие механизмы сортировки в Java
- Collections.sort(List)
- List.sort(Comparator)
- Arrays.sort / Arrays.parallelSort
- TreeSet / TreeMap
- PriorityQueue
Сортирует список на месте. Эффективнее по памяти при необходимости изменить существующий список. Возвращает void и изменяет переданную коллекцию.
Метод интерфейса List, похож на Collections.sort, удобнее при наличии ссылки на список.
Сортировка массивов. parallelSort применяет параллельный алгоритм, полезен для больших массивов.
Структуры данных, которые поддерживают отсортированный набор или карту. Поддерживают динамическое добавление с поддержанием порядка, но обход в них стоит дороже по вставке (O(log n)).
Хороша для извлечения минимального/максимального элемента итеративно. Для получения полного упорядочения потребуется извлечение всех элементов.
Когда предпочесть:
- Для in-place сортировки коллекции - Collections.sort или List.sort.
- Для потокового конвейера без изменения исходных данных - Stream.sorted.
- Для постоянной отсортированной структуры с частыми вставками - TreeSet/TreeMap.
Аналоги в других языках и особенности
Короткие примеры и отличия от поведения Java Stream.sorted.
JavaScript
// Array.prototype.sort
const a = ["b","a","c"];
a.sort();
console.log(a);
[ 'a', 'b', 'c' ]
В JS sort изменяет массив на месте. По умолчанию сортирует как строки; для чисел нужен компаратор.
Python
# sorted возвращает новый список, list.sort меняет на месте
print(sorted([3,1,2]))
[1, 2, 3]
sorted возвращает новый объект, сравнимо с Stream.sorted по смыслу, но API отличается.
SQL
SELECT name FROM users ORDER BY age DESC;
-- Набор строк, упорядоченных по возрасту
ORDER BY выполняет сортировку на уровне базы данных, эффективен при индексах.
C# (LINQ)
var result = new[] {3,1,2}.OrderBy(x => x);
Console.WriteLine(string.Join(',', result));
1,2,3
OrderBy возвращает ленивую последовательность; есть OrderByDescending и ThenBy.
PHP
$a = [3,1,2];
sort($a);
print_r($a);
Array ( [0] => 1 [1] => 2 [2] => 3 )
sort изменяет массив на месте. Для пользовательских сравнений - usort.
Go
package main
import (
"fmt"
"sort"
)
func main(){
a := []int{3,1,2}
sort.Ints(a)
fmt.Println(a)
}
[1 2 3]
sort.Ints меняет срез на месте; для пользовательских структур - sort.Slice.
Kotlin
val a = listOf(3,1,2)
println(a.sorted())
[1, 2, 3]
sorted возвращает новый список, sortedWith принимает компаратор.
Lua
a = {3,1,2}
table.sort(a)
for i,v in ipairs(a) do print(v) end
1 2 3
table.sort меняет массив на месте.
Типичные ошибки при использовании Stream.sorted
- Отсутствует Comparable - при вызове
sorted()на объектах без реализацииComparableвозникаетClassCastException.
import java.util.stream.Stream;
record Item(String id) {}
public class Main {
public static void main(String[] args) {
Stream.of(new Item("a"), new Item("b"))
.sorted() // Item не Comparable
.forEach(System.out::println);
}
}
Exception in thread "main" java.lang.ClassCastException: Main$Item cannot be cast to java.lang.Comparable
Stream.of(1,2).sorted(null).count();
Exception in thread "main" java.lang.NullPointerException
List list = new ArrayList<>(List.of(3,1,2));
list.stream().sorted().collect(Collectors.toList());
System.out.println(list);
[3, 1, 2]
Изменения и история
Метод Stream.sorted был введен в Java 8 вместе со Stream API. С тех пор сигнатуры не менялись. Примитивные специализированные сортировки (IntStream.sorted и т.д.) также появились в Java 8. В последующих релизах JDK производились внутренние оптимизации реализации потоковых операций и алгоритмов сортировки, но публичный API остался стабильным. Рекомендация - проверять улучшения производительности новых JDK при работе с большими параллельными потоками.
Расширенные и редкие сценарии использования
1) Множественные ключи и стабильность
import java.util.stream.Stream;
import java.util.Comparator;
record Person(String name, int age) {}
public class Main {
public static void main(String[] args) {
Stream.of(new Person("Bob", 30), new Person("Ann", 30), new Person("Bob", 25))
.sorted(Comparator.comparing(Person::name).thenComparing(Person::age))
.forEach(p -> System.out.println(p.name() + ":" + p.age()));
}
}
Ann:30 Bob:25 Bob:30
thenComparing применяется для вторичного порядка; сортировка сохраняет порядок для равных ключей.
2) Топ-N через sorted + limit
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.of(5,1,9,3,7)
.boxed()
.sorted(Comparator.reverseOrder())
.limit(3)
.forEach(System.out::print);
}
}
975
Недостаток: сначала сортируются все элементы. Для больших данных можно использовать структуру PriorityQueue для поддержания N лучших элементов без полной сортировки.
3) Топ-N с PriorityQueue - экономия памяти
import java.util.*;
import java.util.stream.IntStream;
public class Main {
public static List topN(IntStream stream, int n){
PriorityQueue pq = new PriorityQueue<>();
stream.forEach(x -> {
if(pq.size() < n) pq.add(x);
else if (x > pq.peek()){
pq.poll();
pq.add(x);
}
});
List res = new ArrayList<>(pq);
res.sort(Comparator.reverseOrder());
return res;
}
public static void main(String[] args){
System.out.println(topN(IntStream.of(5,1,9,3,7), 3));
}
}
[9, 7, 5]
4) Локализованная сортировка строк с Collator
import java.text.Collator;
import java.util.*;
public class Main {
public static void main(String[] args) {
List names = List.of("Łukasz", "Lukas", "Łukasz");
Collator coll = Collator.getInstance(new Locale("pl"));
names.stream()
.sorted(coll)
.forEach(System.out::println);
}
}
Lukas Łukasz Łukasz
5) Сортировка Map по значениям
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args){
Map m = Map.of("a",3, "b",1, "c",2);
var sorted = m.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toList());
System.out.println(sorted);
}
}
[b=1, c=2, a=3]
6) Сортировка в параллельном потоке - предупреждение
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.range(0, 1_000_000).parallel()
.boxed()
.sorted() // возможна высокая нагрузка памяти
.count();
}
}
1000000
Для очень больших наборов стоит оценить потребление памяти и рассмотреть внешнюю сортировку или использование баз данных.
7) Сортировка со сравнением по вычисляемому ключу без создания промежуточных объектов
import java.util.stream.Stream;
import java.util.Comparator;
record Item(String id, int score) {}
public class Main {
public static void main(String[] args) {
Stream.of(new Item("a",10), new Item("b",5))
.sorted(Comparator.comparingInt(Item::score))
.forEach(i -> System.out.print(i.id()));
}
}
ba
comparingInt/comparingLong/comparingDouble избегают лишнего автобоксинга и эффективнее для примитивных ключей.