Основы Stream API
Stream описывает обработку данных
Stream — не новая коллекция и не контейнер, в котором хранятся элементы.
Это объект, описывающий последовательность вычислений над источником данных.
Источником может быть, например, коллекция, массив или специально созданный stream.
List<String> names = List.of("Ann", "Bob", "Alexander");
List<String> longNames = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.toList();
В этой цепочке names остаётся обычным списком.
stream() создаёт stream над его элементами,
затем описываются шаги обработки, а toList() получает итоговый результат.
Исходная коллекция при этом не изменяется.
Pipeline: источник, промежуточные шаги, терминал
Типичный stream-pipeline состоит из трёх частей:
- источник — откуда приходят элементы;
- промежуточные операции — как элементы фильтруются или преобразуются;
- терминальная операция — какой результат нужно получить.
Промежуточные операции возвращают новый stream и сами по себе вычисление не запускают. Работа начинается, когда появляется терминальная операция. Благодаря этому Java может обрабатывать элементы по мере необходимости, а не создавать отдельную временную коллекцию после каждого шага.
Stream ленивый
Stream<String> stream = names.stream()
.filter(name -> {
System.out.println("check: " + name);
return name.length() > 3;
});
System.out.println("pipeline created");
long count = stream.count();
Сообщения check: ... начнут появляться только во время count().
До терминальной операции pipeline лишь описан.
Позже отдельно разберём, какие операции являются промежуточными, а какие терминальными.
Stream используется один раз
После терминальной операции stream считается потреблённым.
Нельзя сохранить его в переменную, вызвать count(),
а затем попытаться пройти тем же stream ещё раз.
Если обработка нужна повторно, создаётся новый stream из источника.