Ответ
Поток (Stream) в Java состоит из трех логических частей, образующих конвейер обработки:
1. Источник (Source) Создает поток из данных. Источники бывают:
- Коллекции:
collection.stream() - Массивы:
Arrays.stream(array) - Значения:
Stream.of("a", "b", "c") - Файлы:
Files.lines(path) - Генераторы:
Stream.iterate(),Stream.generate()
2. Промежуточные операции (Intermediate Operations) Преобразуют элементы потока, возвращая новый поток. Ленивые — выполняются только при вызове терминальной операции.
filter(Predicate<T>)— фильтрация.map(Function<T, R>)— преобразование.sorted()— сортировка.distinct()— удаление дубликатов.limit(long n)— ограничение количества элементов.
3. Терминальная операция (Terminal Operation) Завершает поток, запускает выполнение всего конвейера и производит результат или побочный эффект. Энергичная.
collect(Collector)— агрегация в коллекцию.forEach(Consumer)— выполнение действия для каждого элемента.reduce()— свертка к одному значению.count(),anyMatch(),findFirst()— получение результата.
Пример конвейера:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");
List<String> result = names.stream() // Источник
.filter(name -> name.startsWith("A")) // Промежуточная операция
.map(String::toUpperCase) // Промежуточная операция
.collect(Collectors.toList()); // Терминальная операция
// result = ["ALICE", "ANNA"] Ответ 18+ 🔞
Смотри, чтобы не сойти с ума от этих ваших стримов, давай разложу по полочкам, как это всё работает, без этой охуевшей академической зауми.
Представь себе, что стрим — это такой конвейер на заводе, только вместо болванок у тебя данные бегут. И состоит он из трёх чётких этапов, и если их перепутать — пиздец, ничего не заработает.
1. Источник (Source) — откуда данные берутся, ёпта. Это начало всего, точка старта. Как будто ты подъехал к складу и начал грузить сырьё на ленту. Вариантов дохуя:
- Из коллекции:
список.stream()— элементарно, Ватсон. - Из массива:
Arrays.stream(массив)— тоже понятно. - Из конкретных значений:
Stream.of("раз", "два", "три")— вот тебе готовый набор. - Из файла:
Files.lines(путь_к_файлу)— читаем построчно, красота. - Из генератора:
Stream.iterate()илиStream.generate()— когда надо нагенерировать данных овердохуища, хоть до второго пришествия.
2. Промежуточные операции (Intermediate Operations) — это где мы с данными хуйню делаем. Вот тут вся магия и происходит. Ты ставишь на конвейер станки, которые каждый элемент обрабатывают. ВАЖНОЕ ЗАМЕЧАНИЕ, БЛЯДЬ! Эти операции — ленивые, как мой кот по утрам. Они нихуя не делают, пока ты не скажешь «работай!». Просто готовят план работ.
filter(условие)— отсеивает всё, что не подходит. Как решето, ёбта.map(функция_преобразования)— превращает один элемент в другой. БылInteger, сталString— волшебство, но на практике.sorted()— сортирует. Внезапно, да?distinct()— выкидывает повторы. Один и тот же элемент дважды не проскочит.limit(n)— говорит: «Хватит, браток, мне только первых N штук».
3. Терминальная операция (Terminal Operation) — точка невозврата, где всё заканчивается. Вот это та самая команда «работай!», которая пинает весь этот ленивый конвейер в жопу. После неё поток закрыт, с ним больше ничего сделать нельзя. Она энергичная — сразу всех заставляет шевелиться.
collect(сборщик)— самый частый гость. Собирает результат в коллекцию (List,Set,Map), строку или ещё куда.forEach(действие)— делает что-то с каждым элементом (например, печатает). Результата не возвращает, только побочный эффект.reduce()— сводит весь поток к одному-единственному значению (сумма, максимум).count(),anyMatch(),findFirst()— быстрые проверки: посчитать, есть ли хоть один подходящий, найти первый.
А теперь смотри, как это в жизни выглядит, на простом примере:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");
List<String> result = names.stream() // 1. Источник: грузим имена на конвейер
.filter(name -> name.startsWith("A")) // 2. Промежуточная: фильтр — только на "A"
.map(String::toUpperCase) // 2. Промежуточная: маппер — делаем большими буквами
.collect(Collectors.toList()); // 3. Терминальная: собираем в список — ВСЁ, ПРОЦЕСС ПОШЁЛ!
// В result теперь лежит ["ALICE", "ANNA"]
Короче, алгоритм простой, блядь: Создал поток -> Настроил обработку -> Запустил и получил результат. Главное — не пытаться использовать поток после терминальной операции, а то получишь IllegalStateException и будешь сидеть, чесать репу.