Stream API и Lambda
Поведение как часть программы
До этого момента код в основном отвечал на вопрос «какие данные у нас есть и какие методы их обрабатывают». В этом разделе появится ещё одна важная идея: само действие можно передавать как значение, а обработку последовательности данных — описывать как цепочку шагов. Именно на этом строятся lambda-выражения и Stream API.
После раздела привычная задача вроде «взять список, оставить подходящие элементы, преобразовать их и получить результат» будет читаться как последовательный сценарий, а не как набор вложенных циклов и временных коллекций.
Что будет внутри раздела
- Lambda-выражения. Научимся компактно описывать небольшое поведение прямо в месте использования.
- Функциональные интерфейсы. Разберём типы, в которые помещаются lambda-выражения, и стандартные интерфейсы из
java.util.function. - Ссылки на методы. Увидим короткую запись для случаев, когда lambda только вызывает уже существующий метод или конструктор.
- Основы Stream API. Разберём источник, pipeline, ленивое выполнение и отличие stream от коллекции.
- Промежуточные операции. Научимся фильтровать, преобразовывать, сортировать и ограничивать поток данных.
- Терминальные операции. Посмотрим, как pipeline запускается и превращается в число, значение, коллекцию или побочный эффект.
- Collectors. Соберём результаты в более сложные структуры: строки, группы и отображения.
- Optional. Разберём явное представление ситуации, когда результат может отсутствовать.
- Примитивные стримы. Познакомимся с
IntStream,LongStreamиDoubleStream. - Параллельные стримы. Поймём, что меняется при параллельном выполнении и почему оно подходит далеко не каждой задаче.
Темы идут в этом порядке не случайно. Сначала нужно научиться передавать поведение, затем понять типы этого поведения, а уже после — использовать его в stream-pipeline. В конце раздела мы добавим инструменты для сборки результатов, отсутствующих значений, примитивов и параллельного выполнения.
List<String> names = List.of("Ann", "Bob", "Alexander");
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.toList();
Пока достаточно увидеть общую форму: данные проходят через несколько понятных шагов, а в конце получается новый результат. Как именно работает каждый элемент этой цепочки, будем разбирать последовательно на следующих страницах.