← Назад к вопросам

Как сортировать Stream

1.0 Junior🔥 221 комментариев
#Stream API и функциональное программирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как сортировать Stream в Java

Stream API в Java 8+ предоставляет мощный и гибкий инструмент для сортировки данных. Рассмотрим все основные способы.

Базовая сортировка с sorted()

Сортировка примитивных типов:

List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);

// Восходящий порядок (по умолчанию)
List<Integer> sorted = numbers.stream()
    .sorted()
    .collect(Collectors.toList());
// Результат: [1, 2, 3, 5, 8, 9]

// Нисходящий порядок
List<Integer> reversed = numbers.stream()
    .sorted(Collections.reverseOrder())
    .collect(Collectors.toList());
// Результат: [9, 8, 5, 3, 2, 1]

Сортировка строк:

List<String> words = Arrays.asList("zebra", "apple", "banana");

// По алфавиту
List<String> sorted = words.stream()
    .sorted()
    .collect(Collectors.toList());
// Результат: [apple, banana, zebra]

// Игнорируя регистр
List<String> sortedIgnoreCase = words.stream()
    .sorted(String::compareToIgnoreCase)
    .collect(Collectors.toList());

// По длине строки
List<String> sortedByLength = words.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());
// Результат: [apple, zebra, banana]

Сортировка объектов

Сортировка по одному полю:

public class User {
    private Long id;
    private String name;
    private int age;
    
    // Constructor, getters, setters
}

List<User> users = Arrays.asList(
    new User(1L, "Alice", 28),
    new User(2L, "Bob", 25),
    new User(3L, "Charlie", 32)
);

// Сортировка по имени
List<User> sortedByName = users.stream()
    .sorted(Comparator.comparing(User::getName))
    .collect(Collectors.toList());

// Сортировка по возрасту
List<User> sortedByAge = users.stream()
    .sorted(Comparator.comparingInt(User::getAge))
    .collect(Collectors.toList());

// Сортировка в обратном порядке
List<User> sortedByAgeDesc = users.stream()
    .sorted(Comparator.comparingInt(User::getAge).reversed())
    .collect(Collectors.toList());

Многоуровневая сортировка

Сортировка по нескольким критериям:

public class Employee {
    private String department;
    private String name;
    private double salary;
}

List<Employee> employees = Arrays.asList(...);

// Сортировка: сначала по department, потом по salary (убывающий)
List<Employee> sorted = employees.stream()
    .sorted(Comparator
        .comparing(Employee::getDepartment)
        .thenComparingDouble(Employee::getSalary).reversed())
    .collect(Collectors.toList());

// Более сложный пример
List<Employee> sorted2 = employees.stream()
    .sorted(Comparator
        .comparing(Employee::getDepartment)           // По отделу
        .thenComparingInt(e -> e.getName().length()) // По длине имени
        .thenComparingDouble(Employee::getSalary))    // По зарплате
    .collect(Collectors.toList());

Пользовательские компараторы

Написание собственного Comparator:

// Вариант 1: Через лямбду
List<String> words = Arrays.asList("apple", "pie", "zoo");
List<String> sorted = words.stream()
    .sorted((a, b) -> Integer.compare(b.length(), a.length())) // По убыванию длины
    .collect(Collectors.toList());

// Вариант 2: Через метод
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());
List<String> sorted2 = words.stream()
    .sorted(byLength.reversed())
    .collect(Collectors.toList());

// Вариант 3: Класс с implements Comparator
public class UserAgeComparator implements Comparator<User> {
    @Override
    public int compare(User u1, User u2) {
        return Integer.compare(u1.getAge(), u2.getAge());
    }
}

List<User> sorted3 = users.stream()
    .sorted(new UserAgeComparator())
    .collect(Collectors.toList());

Сортировка с null значениями

Обработка null элементов:

List<String> words = Arrays.asList("apple", null, "zebra", "banana");

// Null значения в конце
List<String> sorted1 = words.stream()
    .sorted(Comparator.nullsLast(Comparator.naturalOrder()))
    .collect(Collectors.toList());
// Результат: [apple, banana, zebra, null]

// Null значения в начале
List<String> sorted2 = words.stream()
    .sorted(Comparator.nullsFirst(Comparator.naturalOrder()))
    .collect(Collectors.toList());
// Результат: [null, apple, banana, zebra]

// Для объектов
List<User> users2 = new ArrayList<>(users);
users2.add(null);

List<User> sorted = users2.stream()
    .sorted(Comparator.nullsLast(
        Comparator.comparing(User::getName)
    ))
    .collect(Collectors.toList());

Производительность сортировки

Важные моменты:

// ❌ Плохо — сортирует всю коллекцию
List<User> users = ...;
List<User> top10 = users.stream()
    .sorted(Comparator.comparingInt(User::getAge).reversed())
    .limit(10)
    .collect(Collectors.toList());

// ✅ Хорошо для больших данных
List<User> top10Better = users.stream()
    .min(Comparator.comparing(Comparator.comparingInt(User::getAge).reversed())) // или использовать TreeSet
    .collect(Collectors.toList());

// ✅ Или использовать PriorityQueue для топ N
PriorityQueue<User> topUsers = users.stream()
    .reduce(new PriorityQueue<>(10, Comparator.comparingInt(User::getAge)),
        (pq, user) -> { pq.offer(user); if(pq.size() > 10) pq.poll(); return pq; },
        (pq1, pq2) -> { pq1.addAll(pq2); return pq1; });

Сортировка с фильтрацией

Комбинирование операций:

List<User> result = users.stream()
    .filter(u -> u.getAge() > 25)           // Фильтр
    .sorted(Comparator.comparing(User::getName)) // Сортировка
    .limit(5)                               // Лимит
    .collect(Collectors.toList());

Параллельная сортировка Stream

Использование parallelStream():

List<Integer> largeList = IntStream.rangeClosed(1, 1_000_000)
    .boxed()
    .collect(Collectors.toList());

// Последовательная сортировка
long start = System.nanoTime();
List<Integer> sorted1 = largeList.stream()
    .sorted()
    .collect(Collectors.toList());
long sequential = System.nanoTime() - start;

// Параллельная сортировка (для больших объёмов)
start = System.nanoTime();
List<Integer> sorted2 = largeList.parallelStream()
    .sorted()
    .collect(Collectors.toList());
long parallel = System.nanoTime() - start;

System.out.println("Sequential: " + sequential + "ns");
System.out.println("Parallel: " + parallel + "ns");

Рекомендации по параллельной сортировке:

  • Используй для больших коллекций (>10,000 элементов)
  • Сортировка уже параллелизована внутри (Fork/Join pool)
  • Перед sorted() не используй expensive фильтры

Практический пример: Сортировка в реальном приложении

@Service
public class UserService {
    public List<UserResponse> getUsersSorted(String sortBy, String order) {
        List<User> users = userRepository.findAll();
        
        Comparator<User> comparator = switch(sortBy) {
            case "name" -> Comparator.comparing(User::getName);
            case "age" -> Comparator.comparingInt(User::getAge);
            case "email" -> Comparator.comparing(User::getEmail);
            default -> Comparator.comparing(User::getId);
        };
        
        if ("desc".equalsIgnoreCase(order)) {
            comparator = comparator.reversed();
        }
        
        return users.stream()
            .sorted(comparator)
            .map(UserResponse::fromUser)
            .collect(Collectors.toList());
    }
}

Итог: Stream API предоставляет гибкий механизм для сортировки через sorted() метод с поддержкой Comparator. Можно использовать встроенные компараторы, писать свои, объединять несколько критериев и обрабатывать null значения.

Как сортировать Stream | PrepBro