Ответ
Операции в Stream API делятся на промежуточные (intermediate) и терминальные (terminal), что является основой ленивой (lazy) обработки данных.
Промежуточные операции:
- Цель: Преобразовать или отфильтровать поток элементов, создавая новый поток для дальнейшей обработки.
- Поведение: Ленивые. Они не выполняют немедленных вычислений, а лишь добавляют операцию в конвейер (pipeline).
- Возвращаемое значение:
Stream<T>. - Примеры:
filter(Predicate<T>),map(Function<T, R>),sorted(),distinct(),limit(long n),peek(Consumer<T>).
Терминальные операции:
- Цель: Завершить обработку потока, получив конечный результат (значение, коллекцию или побочный эффект).
- Поведение: Энергичные (eager). Вызов терминальной операции запускает выполнение всего накопленного конвейера промежуточных операций.
- Возвращаемое значение: Не
Stream(это может бытьvoid,Optional,List,boolean,longи т.д.). - Примеры:
collect(Collector),forEach(Consumer<T>),reduce(...),count(),anyMatch(Predicate<T>),findFirst().
Ключевое следствие: Поскольку промежуточные операции ленивы, поток данных будет обработан за один проход (при вызове терминальной операции), что может быть эффективнее нескольких итераций по коллекции.
Пример, демонстрирующий ленивость:
List<String> list = Arrays.asList("a1", "b2", "c3", "a2");
list.stream()
.filter(s -> {
System.out.println("filter: " + s); // Вывод появится только после вызова .findFirst()
return s.startsWith("a");
})
.map(s -> {
System.out.println("map: " + s); // Аналогично
return s.toUpperCase();
})
.findFirst(); // Терминальная операция, которая запускает обработку
// Вывод в консоль:
// filter: a1
// map: a1
// Обработка остановилась после нахождения первого подходящего элемента. Ответ 18+ 🔞
Давай разберёмся с этим Stream API, а то народ часто путается, как будто на дворе 2002-й год и все только про коллекции знают. Тут, блядь, всё строится на двух типах операций: промежуточные и терминальные. И это, ёпта, основа всей их ленивой хуйни, которая делает обработку эффективной.
Промежуточные операции — это как разведка перед боем:
- Задача у них: Поковырять поток, отфильтровать что-то, преобразовать — создать новый поток для следующих приключений.
- Как работают: Абсолютно ленивые, сука. Они нихуя не делают сразу. Просто записывают в блокнотик: «так, чувак, потом вот это проверь, а вот это преобразуй». Возвращают они новый
Stream<T>, чтобы цепочку можно было строить. - Примеры, которые все знают:
filter()(отсеять мусор),map()(преобразовать одно в другое),sorted()(отсортировать),distinct()(убрать повторы).peek()— это вообще хитрая жопа, которая позволяет подсмотреть за элементом, не трогая его.
Терминальные операции — это уже штурм:
- Задача: Всё закончить. Получить какой-то итог: коллекцию, одно значение, или просто натворить дел (побочные эффекты).
- Как работают: Вот тут начинается движ. Вызов терминальной операции — это как крик «ПОШЛИ!». В этот момент запускается выполнение всего накопленного плана, всех этих ленивых промежуточных штук.
- Что возвращают: Всё что угодно, кроме
Stream. Можетvoid,Optional,List, да хотьboolean.collect(),forEach(),reduce(),count()— вот это всё они.
Главная фишка, которую надо понять: Из-за этой ленивости весь конвейер выполняется за ОДИН проход по данным. Не так, что сначала один фильтр прогнал всю коллекцию, потом второй, потом маппинг. Всё происходит потоком, что часто овердохуища эффективнее.
Смотри пример, где видна эта лень, блядь:
List<String> list = Arrays.asList("a1", "b2", "c3", "a2");
list.stream()
.filter(s -> {
System.out.println("filter: " + s); // Этого вывода не будет, пока...
return s.startsWith("a");
})
.map(s -> {
System.out.println("map: " + s); // ...пока не дадим команду!
return s.toUpperCase();
})
.findFirst(); // А вот она, терминальная операция! Всё поехало.
// В консоли увидим:
// filter: a1
// map: a1
// И всё. Нашли первый подходящий — и приехали. На "b2", "c3" и "a2" даже filter не сработал. Умно, да?
Вот и вся магия. Сначала настраиваешь конвейер (лениво), а потом одним пинком (findFirst(), collect()) запускаешь всю эту машину. Главное — не забывать этот пинок дать, а то можно построить целую схему, а результата нихуя не получить, сам от себя охуев.