Ответ
Stream API — это абстракция для декларативной обработки последовательностей данных (коллекций, массивов, I/O каналов) в функциональном стиле, добавленная в Java 8.
Ключевые концепции:
- Не изменяет источник: Операции над Stream работают с его элементами, но не модифицируют исходную коллекцию.
- Ленивые вычисления: Промежуточные операции (intermediate) выполняются только при вызове терминальной операции.
- Конвейер операций: Stream строится как цепочка: источник → промежуточные операции → терминальная операция.
- Одноразовость: Stream нельзя использовать повторно после вызова терминальной операции.
Типы операций:
- Промежуточные (Intermediate): Возвращают новый Stream (ленивые).
filter(Predicate<T>)– фильтрация по условию.map(Function<T, R>)– преобразование каждого элемента.sorted()– сортировка.distinct()– удаление дубликатов.
- Терминальные (Terminal): Запускают выполнение конвейера и возвращают результат (не Stream).
collect(Collector)– агрегация в коллекцию или строку.forEach(Consumer)– выполнение действия для каждого элемента.reduce()– свертка элементов в одно значение.count(),anyMatch(),allMatch()– агрегатные проверки.
Пример:
List<String> names = Arrays.asList("Anna", "Bob", "Alice", "David", "Alex");
List<String> result = names.stream() // Источник
.filter(name -> name.length() > 3) // Промежуточная: оставить имена длиннее 3 букв
.map(String::toUpperCase) // Промежуточная: преобразовать в верхний регистр
.sorted() // Промежуточная: отсортировать
.collect(Collectors.toList()); // Терминальная: собрать в список
System.out.println(result); // Вывод: [ALEX, ALICE, ANNA, DAVID]
Параллельные потоки: Для обработки больших данных можно использовать parallelStream() для автоматического распараллеливания операций по ядрам процессора.
Ответ 18+ 🔞
А, Stream API, говоришь? Ну это ж та самая штука, которая в Java 8 приехала и всех так охуевать заставила. Типа, раньше мы коллекции в циклах крутили, как обезьяны, а теперь можно красиво, декларативно, почти как на модных языках. В общем, мартышлюшка стала чуть менее волосатой.
Смотри, в чём суть, чтобы не пиздеть просто так. Это абстракция, такая надстройка, чтобы данные обрабатывать — коллекции там, массивы — но не тыкая в них палкой-итератором, а описывая что сделать, а не как. Красота, блядь.
Основные пиздели, которые надо в башке держать:
- Источник не трогает! Это святое. Stream работает с данными, но исходную коллекцию он не портит. Сделал что-то — получил новый поток или результат. Исходник остался цел и девственен, как снег.
- Ленивый, как мой кот сутра. Все промежуточные операции (их ещё intermediate зовут) нихуя не делают, пока ты не позовёшь операцию терминальную. Они просто в цепочку выстраиваются и ждут команды «поехали». Экономия, блядь, ресурсов — зачем делать, если результат не нужен?
- Конвейер, ёпта. Всё строится, как на заводе: взял источник -> навешал кучу операций-фильтров-преобразований -> в конце получил готовый продукт. Элегантно, сука.
- Одноразовый, как шприц. Вызвал терминальную операцию — поток схлопнулся. Хочешь ещё раз пробежаться — создавай новый. Нельзя повторно использовать, иначе получишь
IllegalStateException— вот тебе и вся магия нахуй.
Теперь про операции, их два лагеря:
-
Промежуточные (Лентяи). Возвращают новый Stream, чтоб цепочку продолжать.
filter(Predicate<T>)— отсеивает по условию. Нужно только тех, кто условие проходит. Остальных — нахуй.map(Function<T, R>)— превращает каждый элемент из одного в другое. БылString, сталInteger. Фокус-покус.sorted()— сортирует. Думал, всё просто? А если компаратор свой? Вот тут-то и начинается веселье.distinct()— убирает повторы. Оставляет только уникальных пассажиров.
-
Терминальные (Работяги). Запускают весь этот ленивый конвейер и выдают финальный результат. После них — пиздец потоку.
collect(Collector)— собирает всё в кучу: в список, в сет, в мапу, в строку. Самый частый гость.forEach(Consumer)— делает что-то с каждым элементом. Для сайд-эффектов, типа вывода в консоль.reduce()— сводит всю последовательность к одному значению. Сумма, максимум, конкатенация — всё тут.count(),anyMatch(),allMatch()— проверки на вшивость. «Хотя бы один?», «Все ли?», «Скво?».
Ну и пример, чтобы не быть пиздаболом:
List<String> names = Arrays.asList("Anna", "Bob", "Alice", "David", "Alex");
List<String> result = names.stream() // Вот он, источник, список имён
.filter(name -> name.length() > 3) // Отфильтровали короткие имена (Bob, прости)
.map(String::toUpperCase) // Перевели всё в крик души (верхний регистр)
.sorted() // Отсортировали по алфавиту
.collect(Collectors.toList()); // И собрали обратно в список — готово!
System.out.println(result); // Вывод: [ALEX, ALICE, ANNA, DAVID]
Видишь? Ни одного for, ни одного итератора. Чисто описание что нужно: взять, отфильтровать, намапить, отсортировать, собрать. Красота, блядь.
А, да, забыл про пушку — параллельность. Если список овердохуища большой, можно вместо stream() вызвать parallelStream(). И тогда обработка размажется по ядрам процессора сама собой. Но это уже тема для отдельного разговора, потому что там свои грабли: синхронизация, производительность, которая может и просесть, если не угадать. В общем, волнение ебать, но штука мощная.