Что получается на выходе промежуточной операции map в Stream в Java
Что проверяют
Понимаете ли вы, что стрим ленивый и типизированный: что именно возвращает map, когда это выполняется и чем операция отличается от flatMap. Вопрос короткий, но по ответу видно, писал ли человек цепочки сам.
Как отвечать
Сигнатура такая: <R> Stream<R> map(Function<? super T, ? extends R> mapper). На выходе новый стрим того же размера, но с элементами типа R. Это промежуточная операция, поэтому в момент вызова не происходит ничего: функция не применяется, пока не позвана терминальная операция.
List<String> names = List.of("ann", "bobby");
List<Integer> lengths = names.stream()
.map(String::length) // Stream<Integer>, ещё ничего не считается
.toList(); // здесь элементы проходят по конвейеруОбработка идёт поэлементно, а не слоями: каждый элемент проходит всю цепочку до конца, поэтому короткое замыкание вроде findFirst не даст применить функцию ко всему источнику.
Для примитивов есть mapToInt, mapToLong, mapToDouble: они дают специализированные стримы и убирают упаковку, вернуться назад можно через boxed. Если функция сама возвращает стрим или коллекцию, нужен flatMap, он разворачивает вложенность.
Где спотыкаются
Говорят, что исходный поток не меняется, и делают из этого вывод, что его можно использовать снова. Нельзя: после терминальной операции повторный вызов даёт IllegalStateException. Переиспользуют источник данных, а новый стрим создают заново.
Второе: пишут в функцию побочные эффекты, например складывают результат во внешний список или ходят в базу на каждый элемент. Функция должна быть без состояния, иначе на параллельном стриме получится гонка, а лишние запросы всплывут на проде.
Третье: путают map и flatMap и получают вложенный стрим, который потом невозможно собрать. Та же ловушка есть у Optional: map на функции, возвращающей Optional, даст Optional внутри Optional.