Ответ
Промежуточные операции (intermediate operations) в Stream API возвращают новый поток и являются "ленивыми" — они выполняются только при вызове терминальной операции.
Основные промежуточные операции:
| Операция | Сигнатура | Назначение |
|---|---|---|
| Фильтрация | filter(Predicate<T>) |
Отбирает элементы, удовлетворяющие условию. |
| Преобразование | map(Function<T, R>) |
Преобразует каждый элемент в другой объект. |
| "Разворачивание" | flatMap(Function<T, Stream<R>>) |
Преобразует элемент в поток и объединяет все потоки в один. |
| Уникальность | distinct() |
Удаляет дубликаты (использует equals() и hashCode()). |
| Сортировка | sorted() или sorted(Comparator<T>) |
Сортирует элементы (естественным порядком или компаратором). |
| Просмотр | peek(Consumer<T>) |
Выполняет действие над каждым элементом без изменения потока (для отладки). |
| Ограничение | limit(long maxSize) |
Ограничивает поток первыми N элементами. |
| Пропуск | skip(long n) |
Пропускает первые N элементов потока. |
Практические примеры:
List<String> names = List.of("John", "Alice", "Bob", "Alice", "Charlie");
// Пример 1: Фильтрация, преобразование, удаление дубликатов
List<String> result = names.stream()
.filter(name -> name.length() > 3) // Оставляет "John", "Alice", "Charlie"
.map(String::toUpperCase) // Преобразует в "JOHN", "ALICE", "CHARLIE"
.distinct() // Удаляет дубликаты (если бы были)
.collect(Collectors.toList()); // Терминальная операция
// Результат: ["JOHN", "ALICE", "CHARLIE"]
// Пример 2: Использование flatMap для работы с вложенными структурами
List<List<Integer>> listOfLists = List.of(
List.of(1, 2),
List.of(3, 4, 5)
);
List<Integer> flattened = listOfLists.stream()
.flatMap(List::stream) // Преобразует каждый List в Stream и объединяет
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5]
// Пример 3: Отладка с помощью peek
long count = names.stream()
.peek(name -> System.out.println("Before filter: " + name))
.filter(name -> name.startsWith("A"))
.peek(name -> System.out.println("After filter: " + name))
.count();
// Вывод в консоль поможет отследить путь элементов через поток.
Важно: Промежуточные операции можно объединять в цепочки, создавая декларативное описание pipeline для обработки данных.