что такое Stream в Java ?
Stream в Java — это абстракция, представляющая последовательность элементов и позволяющая обрабатывать её в функциональном стиле. API добавлен в Java 8 и находится в пакете java.util.stream. Основная цель — упростить работу с коллекциями, избавив от явных циклов и управления итерациями: фильтрация, сортировка, преобразование и агрегация выполняются декларативно, через цепочки операций.
Как это работает
Stream не хранит данные, а берёт их из источника (коллекция, массив, файл, генератор) и пропускает через конвейер операций. Ключевые особенности:
- Не изменяет исходные данные — все преобразования возвращают новый поток или результат, исходная коллекция остаётся нетронутой.
- Ленивая обработка — промежуточные операции не выполняются, пока не вызвана терминальная. Это позволяет оптимизировать вычисления, например, для
findFirst()обработается только нужное количество элементов. - Параллельность — вызов
parallelStream()илиparallel()на потоке включает многопоточную обработку, что ускоряет работу на многоядерных системах.
Операции делятся на два типа:
- Промежуточные (intermediate) — возвращают новый поток, позволяя строить цепочки. Примеры:
filter,map,sorted,distinct,limit. - Терминальные (terminal) — завершают конвейер и возвращают результат:
collect,reduce,count,forEach,findFirst,anyMatch.
Пример
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("apple", "banana", "cherry", "date");
// Фильтрация строк длиннее 5 символов и сбор их длин в список
List<Integer> lengths = strings.stream()
.filter(s -> s.length() > 5) // промежуточная операция
.map(String::length) // промежуточная операция
.collect(Collectors.toList()); // терминальная операция
System.out.println(lengths); // [6, 6]
}
}Здесь stream() создаёт поток из списка, filter оставляет только строки длиннее 5 символов, map преобразует каждую строку в её длину, а collect собирает результат в новый список.
Подводные камни
- Одноразовость — поток нельзя использовать повторно после терминальной операции; для новой обработки нужно создавать поток заново.
- Ленивость — без терминальной операции промежуточные не выполняются, поэтому цепочка без
collectилиforEachпросто не сработает. - Параллельные потоки — не всегда быстрее: на маленьких данных накладные расходы на синхронизацию могут превысить выигрыш. Используйте
parallelStream()осознанно. - Побочные эффекты — избегайте изменения внешнего состояния внутри лямбда-выражений, это нарушает функциональный стиль и может привести к ошибкам при параллельном выполнении.
nullв потоке —Stream.of(null)выброситNullPointerException; для работы с nullable-значениями используйтеOptional.
Когда использовать
Stream подходит для декларативной обработки коллекций: фильтрация, преобразование, агрегация, группировка. Особенно полезен при работе с большими объёмами данных, когда нужна параллельная обработка, или когда код с циклами становится громоздким. Для простых итераций с побочными эффектами (например, вывод в консоль) обычный цикл for может быть читабельнее.
Коротко
Stream— это абстракция последовательности элементов, добавленная в Java 8 для функциональной обработки данных.- Промежуточные операции (
filter,map,sorted) ленивы и возвращают новый поток; терминальные (collect,reduce,count) запускают вычисления. - Поток не изменяет исходные данные и может работать параллельно через
parallelStream(). - Помните об одноразовости потока и избегайте побочных эффектов в лямбда-выражениях.
