Почему flatMap принимает функцию, которая возвращает поток, а не коллекцию?

Почему для операции flatMap требуется функция, возвращающая Stream, а не функция, возвращающая Collection? По какой-то конкретной причине это заставляет пользователя выполнять преобразование потока вручную?

Читая пример исходного кода, я вижу, что таким образом совместимость может быть расширена до массивов, но не приведет ли перегрузка flatMap к тому же результату?

// Java 8 source code example:
Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" +")));

В каких случаях лучше явно указать процесс потоковой передачи?

Пример: почему я вынужден это делать

Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> flatList = map.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toList());

вместо этого?

Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> flatList = map.entrySet().stream().flatMap(Map.Entry::getValue).collect(Collectors.toList());

может быть¹, потому что flatMap сам по себе предназначен для использования в Stream, что-то вроде согласованности - одна из характеристик Stream заключается в том, чтобы запускать только те части, которые необходимы, когда и если они нужны - создание коллекции позволит избежать этого. || ¹ Я не являюсь ни разработчиком, ни спецификатором, лучше спросить кого-нибудь, кто занимается его разработкой

user16320675 15.11.2022 08:04

Что, если вы захотите вернуть другой поток, который не представлен в виде массива или коллекции? Допустим, я хочу вручную разбить объект на поток значений — конечно, я мог бы сначала создать коллекцию, но это может быть накладным. Простое требование вернуть поток очень гибко и не намного сложнее.

Thomas 15.11.2022 08:05

Потоки и коллекции не взаимозаменяемы. Но вы можете транслировать любую коллекцию, поэтому имеет смысл принять более гибкую конструкцию.

shmosel 15.11.2022 08:07

@Thomas, да, это не добавляет намного больше кода, мне просто было интересно, почему он был разработан таким образом, потому что большую часть времени это просто заставляет меня писать дополнительный .stream(), я думаю, ваш аргумент имеет смысл, Спасибо!

Teddy Tsai 15.11.2022 08:15

Потоковая передача через другую коллекцию, похоже, не является предполагаемым основным вариантом использования, по крайней мере, реализация не оптимизирована для этого сценария. Лучше всего он работает с довольно небольшими подпотоками, которые могут быть получены из коллекций, но также могут быть получены из таких функций, как arg -> condition? Stream.of(x, y, z): null или arg -> condition? Stream.of(arg): Stream.of(arg, anotherObject) Кстати, вместо .flatMap(line -> Stream.of(line.split(" +"))) я бы предпочел использовать .flatMap(Pattern.compile(" +")::splitAsStream)

Holger 15.11.2022 08:58

По моему опыту, он часто используется с коллекциями, но часто перед сведением выполняются дополнительные операции, например. .flatMap(list -> list.stream().limit(3))

shmosel 15.11.2022 09:13

Кстати, если вы считаете, что это чище, вы можете заменить .flatMap(e -> e.getValue().stream()) на .map(Entry::getValue).flatMap(List::stream).

shmosel 15.11.2022 09:19
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
7
121
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему для операции flatMap() требуется функция, которая возвращает Stream, а не функция, которая возвращает Collection?

Тому есть много причин:

  • Поток — это средство итерации, т. е. мы не храним данные в потоке, его цель — лениво перебирать много по источнику данных, который может быть String, массивом, потоком ввода-вывода и т. д.

  • Во-вторых, потоковые операции делятся на две группы: терминальные, предназначенные для выдачи результата и прекращения выполнения потокового конвейера (т. . Промежуточные операции всегда ленивы. Поток берет элементы из источника один за другим и обрабатывает их лениво, т.е. операции происходят только тогда, когда это необходимо. Не делайте новый поток с цепочкой вложенных for-циклов, они ведут себя иначе. Каждая промежуточная операция создает новый поток.

Вот цитата из документации API:

Потоки отличаются от коллекций несколькими способами:

  • Нет хранилища. Поток — это не структура данных, в которой хранятся элементы; вместо этого он передает элементы из источника, такого как структура данных, массив, функция-генератор или канал ввода-вывода через конвейер вычислительных операций.

  • Поиск лени. Многие потоковые операции, такие как фильтрация, сопоставление или удаление дубликатов, могут быть реализованы лениво, подвергая возможности для оптимизации. Например, «найти первую строку с тремя последовательными гласными" не нужно проверять все входные строки. Потоковые операции делятся на промежуточные (Stream-производящие) операции и терминальные (производящие ценность или побочные эффекты) операции. Промежуточные операции всегда ленивы.

  • Поскольку Stream являются внутренними итераторами по источнику данных, которые могут иметь различную природу (не обязательно Collectoin), для flatMap() разумно ожидать данные в предсказуемой унифицированной форме, а не в виде массива, коллекции, итерации и т. д., а другого внутреннего итератора, то есть другой поток, так что очевидно, как с этим бороться.

Любой вариант, который вы можете использовать, будет менее интуитивным. Если бы flatMap() был реализован таким образом, что ожидал бы функцию, производящую Collection, как бы вы поступили со строками, массивами, потоками ввода-вывода, различными реализациями Iterable? Сбрасывая данные в коллекцию - это не вариант. Та же проблема возникнет, если мы представим, что flatMap() требуется Iterable, как мы будем производить Iterable из String? Потоки разработаны, чтобы быть универсальными.

Я подозреваю, что ваше суждение о flatMap() предвзято, потому что вы к нему не привыкли. Когда вы принимаете идею о том, что Stream является внутренним итератором, тот факт, что операция выравнивания данных ожидает функцию, создающую другой итератор, будет восприниматься как более интуитивно понятный.

Другие вопросы по теме