Ответ
Промежуточные операции в Stream API (lazy operations) преобразуют один поток в другой и могут быть объединены в цепочку. Основные из них:
| Операция | Сигнатура | Назначение |
|---|---|---|
filter |
Stream<T> filter(Predicate<? super T>) |
Фильтрует элементы по условию (predicate). |
map |
<R> Stream<R> map(Function<? super T, ? extends R>) |
Преобразует каждый элемент (1 к 1). |
flatMap |
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<R>>) |
Преобразует элемент в поток и "склеивает" все потоки в один (1 ко многим). |
distinct |
Stream<T> distinct() |
Удаляет дубликаты (использует equals()). |
sorted |
Stream<T> sorted() / sorted(Comparator) |
Сортирует элементы (естественным порядком или компаратором). |
peek |
Stream<T> peek(Consumer<? super T>) |
Выполняет действие для каждого элемента (часто для отладки), не изменяя элементы. |
limit |
Stream<T> limit(long maxSize) |
Ограничивает поток первыми N элементами. |
skip |
Stream<T> skip(long n) |
Пропускает первые N элементов. |
Практический пример:
List<String> names = Arrays.asList("John", "Alice", "Bob", "Anna", "Alex");
List<String> result = names.stream()
.filter(name -> name.length() > 3) // Оставляем имена длиннее 3 букв: [John, Alice, Anna, Alex]
.map(String::toUpperCase) // Преобразуем в верхний регистр: [JOHN, ALICE, ANNA, ALEX]
.sorted() // Сортируем: [ALEX, ALICE, ANNA, JOHN]
.distinct() // Здесь дубликатов нет, но операция актуальна
.collect(Collectors.toList()); // Терминальная операция — собираем в список
System.out.println(result); // Вывод: [ALEX, ALICE, ANNA, JOHN]
Все эти операции являются "ленивыми" — вычисления происходят только после вызова терминальной операции (например, collect, forEach).
Ответ 18+ 🔞
Смотри, вот тебе вся эта магия с потоками, но без этой охуевшей академической зауми. Представь, что у тебя есть мешок с данными, и ты его как будто просеиваешь через сито, а потом ещё и красиво упаковываешь.
Вот эти ленивые операции — они как раз и есть твои сита, фильтры и трансформеры. Они ничего не делают, пока ты не скажешь: «Всё, хватит болтать, давай результат!». Это и есть терминальная операция.
Основные прибамбасы:
filter— это твой стражник. «Ты кто такой? Длина имени больше трёх? Проходи. А ты, коротышка, нахуй иди». Чистая проверка.map— это как конвейер на заводе. Берёт каждый элемент, делает с ним что-то одно (например, красит в красный цвет или, как у нас, переводит в верхний регистр) и отправляет дальше. Один на один.flatMap— это уже не конвейер, а хрень посерьёзнее. Берёт элемент, а он, сука, сам как мешок с другими элементами (например, список внутри списка), вытряхивает всё это добро в общий поток и говорит: «Разбирайтесь!». Один ко многим.distinct— эта штука выкидывает повторы. Видит два одинаковыхJOHN— один отправляет в утиль. Используетequals(), так что если твои объекты криво его переопределили, то и distinct будет работать как попало.sorted— ну тут всё ясно, сортировщик. Молчаливый такой, стоит, перекладывает элементы по порядку. Может сам, а можешь дать ему инструкцию (компаратор).peek— самый подозрительный тип. Он ничего не меняет, просто подглядывает. Поставил его в цепочку — и он для каждого элемента делает какое-то действие (чаще всего для отладки, чтобы посмотреть, что по конвейеру едет). Главное — не злоупотреблять, а то можно неожиданно всё сломать.limitиskip— близнецы-братья. Один говорит: «Мне только первых N, остальное не надо», а второй: «Первые N — нахуй не сдались, давай что после них».
А теперь смотри, как это всё вместе охуенно работает на живом примере:
List<String> names = Arrays.asList("John", "Alice", "Bob", "Anna", "Alex");
List<String> result = names.stream() // Запускаем конвейер из списка
.filter(name -> name.length() > 3) // Стражник отсеивает Боба (всего 3 буквы, лох)
.map(String::toUpperCase) // Конвейер красит все имена в ЗАГЛАВНЫЕ БУКВЫ
.sorted() // Молчаливый сортировщик выстраивает по алфавиту
.distinct() // Вышибала на выходе, но дубликатов тут нет
.collect(Collectors.toList()); // ФИНАЛКА! Всё, стоп машина, упаковываем результат в новый список
System.out.println(result); // Вывод: [ALEX, ALICE, ANNA, JOHN]
Весь этот цирк с цепочкой операций — это просто план работ. Реальная работа (filter, map, sorted) начнётся только в тот момент, когда вызовется терминальная операция — в нашем случае это collect(). До этого можно хоть десять map и filter навесить — потоку будет похуй, он даже не шелохнётся. Ленивые они, блядь, до самого конца.