Ответ
Промежуточные (intermediate) операции в Stream API — это ленивые операции, которые преобразуют один поток в другой, формируя конвейер обработки данных. Они выполняются только при вызове терминальной операции.
Ключевые свойства:
- Ленивость (Lazy): Вычисление происходит только по требованию терминальной операции.
- Возвращают новый Stream: Позволяют строить цепочки вызовов.
- Без состояния / с состоянием: Некоторые операции (как
sortedилиdistinct) требуют знания обо всех элементах потока.
Распространенные промежуточные операции:
| Операция | Описание | Пример |
|---|---|---|
filter(Predicate<T>) |
Отфильтровывает элементы по условию. | .filter(s -> s.length() > 3) |
map(Function<T,R>) |
Преобразует каждый элемент. | .map(String::toUpperCase) |
sorted() / sorted(Comparator) |
Сортирует элементы. | .sorted(Comparator.reverseOrder()) |
distinct() |
Удаляет дубликаты. | .distinct() |
limit(long n) |
Ограничивает поток первыми n элементами. |
.limit(10) |
skip(long n) |
Пропускает первые n элементов. |
.skip(5) |
peek(Consumer<T>) |
Выполняет действие для каждого элемента (отладка). | .peek(System.out::println) |
Пример конвейера:
List<String> result = names.stream() // Источник
.filter(name -> name.startsWith("A")) // Промежуточная (ленивая)
.map(String::toUpperCase) // Промежуточная (ленивая)
.sorted() // Промежуточная (ленивая, с состоянием)
.collect(Collectors.toList()); // Терминальная (запускает выполнение)
Важно: Пока не вызвана терминальная операция (collect, forEach, reduce), данные не обрабатываются. Это позволяет оптимизировать выполнение (например, объединение нескольких операций filter в один проход).
Ответ 18+ 🔞
А, слушай, про эти ваши стримы в Java! Ну, это ж просто пиздец, как удобно, если понять, как они там, блядь, под капотом шевелятся. Сейчас разжую, как для дебила, но ты не обижайся.
Представь себе, что у тебя есть труба, по которой сыпется хуёвочка разная — данные там, строки, цифры. Так вот, промежуточные операции — это как раз такие хитрые фильтры и преобразователи, которые ты на эту трубу накручиваешь. Но самый прикол в чём? Пока ты в конец трубы ведро не подставишь (это терминальная операция), нихуя не течёт! Всё стоит сухо, блядь. Это и есть их ленивость (Lazy). Они только готовятся, сука, работать, но сами по себе — ни хуя.
Что они умеют, эти мартышлюшки промежуточные?
- Вернуть новый Stream: Каждая такая операция — это как новый отрезок трубы. Прикрутил
filter— получил трубу только для длинных слов. Прикрутилmap— получил трубу, где всё уже в верхнем регистре летит. Цепочку строить можно — красота, ёпта! - Быть простыми или с памятью: Большинство — пофигисты, работают с элементом и тут же его забывают. Но есть и заумные, типа
sorted()илиdistinct(). Вот эти, блядь, хитрожопые — им надо все элементы сначала увидеть, чтобы отсортировать или дубликаты выкинуть. Такие называются stateful (с состоянием), и они могут производительность подъебать на больших потоках.
Ну и табличка, чтоб не ебать мозг:
| Что вызываем | Что делает, грубо говоря | Пример, как в жизни |
|---|---|---|
filter(...) |
Отсеивает хуйню, которая не подходит под условие. | .filter(s -> s.length() > 3) — оставляет только слова длиннее трёх букв, короткие — нахуй. |
map(...) |
Превращает каждый кусок говна во что-то другое. | .map(String::toUpperCase) — все строки делает КРИЧАЩИМИ, БЛЯДЬ. |
sorted() |
Включает внутреннего менеджера и всё раскладывает по порядку. | .sorted(Comparator.reverseOrder()) — выстраивает всё задом наперёд, с конца. |
distinct() |
Выкидывает повторы, оставляет только уникальные экземпляры. | .distinct() — если два одинаковых элемента приползли, один идёт в пизду. |
limit(n) |
Жадная сволочь. Хватает только первые N штук, остальным — отбой. | .limit(10) — «мне только десять, на остальное насрать». |
skip(n) |
Наоборот, первые N штук пропускает, мол, «мелочь, пошла нахуй». | .skip(5) — первые пять элементов игнорирует, начинает с шестого. |
peek(...) |
Подсматриватель. Позволяет глянуть на элемент, пока он по трубе летит (обычно для отладки). | .peek(System.out::println) — «ой, а что это у нас тут пролетело?». |
И вот живой пример, как это всё в коде выглядит:
List<String> result = names.stream() // Вот она, труба-источник, открыли кран
.filter(name -> name.startsWith("A")) // Фильтр: только на "А". Но пока тока намерение!
.map(String::toUpperCase) // Преобразуем в большие буквы. Всё ещё тишина.
.sorted() // Сортируем. Уже сложнее, но поток ещё не пошёл!
.collect(Collectors.toList()); // А ВОТ ТУТ — ХУЯК! Подставили ведро (collect). И только сейчас вся цепочка ПРОСНУЛАСЬ и начала пахать!
Запомни главное, чувак: пока в конце не появится что-то жадное (collect, forEach, count), весь этот красивый конвейер из filter-map-sorted — это просто сказка, блядь, нарисованная в воздухе. JVM смотрит на эту цепочку и умно оптимизирует, может несколько операций в один проход слепить. Но начинает она это делать только по твоей команде — терминальной операции. Вот такая магия, ёпта!