В чем разница между методами map () и flatMap () в Java 8?

708

В Java 8, в чем разница Stream.map()и Stream.flatMap()методы?

cassiomolin
источник
55
Тип подписи своего рода рассказывает всю историю. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Крис Мартин
99
Кстати, эти сигнатуры типов даже не похожи на Java. (Я знаю, я знаю - но сказать, что это рассказывает «всю историю» относительно карты / flatMap, предполагает много знаний о новой и улучшенной «Java ++»)
Майкл
16
@michael Эта сигнатура типа выглядит как Haskell, а не Java. Но это не ясно , является ли какой - либо более читаемым фактическая Java подпись: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Стюарт Маркс
8
Ха, да, я имел в виду «настоящую Яву». Как и C ++, современная Java практически неузнаваема для всех, кто начал использовать ее в 90-х годах (как и я, оба языка). Просто отвечая на комментарий, сигнатуры методов едва ли рассказывают «целую историю», по крайней мере, больше, без дополнительной экспозиции (или, в случае комментаторов, перевода).
Майкл
2
То есть mapлямбда-преобразователь Ra flatMap'возвращает лямбда-отображение mapper a Streamof R( Stream<R>). Потоки, возвращаемые flatMapпреобразователем, эффективно сцепляются. В противном случае оба mapи flatMapвозвращаются Stream<R>; разница , что возвращается картограф лямбда, Rпротив Stream<R>.
derekm

Ответы:

816

Оба mapи flatMapмогут быть применены к a, Stream<T>и они оба возвращают a Stream<R>. Разница заключается в том, что mapоперация создает одно выходное значение для каждого входного значения, тогда как flatMapоперация создает произвольное число (ноль или более) значений для каждого входного значения.

Это отражено в аргументах каждой операции.

mapОперация занимает Function, которая вызывается для каждого значения во входном потоке и производит одно значение результата, который посылается в выходной поток.

flatMapОперация принимает функцию , которая концептуально хочет потреблять одно значение и произвести произвольное число значений. Тем не менее, в Java для метода трудно возвращать произвольное количество значений, поскольку методы могут возвращать только ноль или одно значение. Можно представить API, в котором функция mapper flatMapпринимает значение и возвращает массив илиListзначений, которые затем отправляются на выход. Учитывая, что это библиотека потоков, особенно удачным способом представления произвольного числа возвращаемых значений является то, что сама функция mapper возвращает поток! Значения из потока, возвращаемого преобразователем, выводятся из потока и передаются в выходной поток. «Скопления» значений, возвращаемых при каждом вызове функции преобразования, вообще не различаются в выходном потоке, поэтому говорят, что вывод «сглажен».

Типичное использование для функции mapper flatMapдля возврата, Stream.empty()если она хочет отправить нулевые значения, или что-то вроде, Stream.of(a, b, c)если она хочет вернуть несколько значений. Но, конечно, любой поток может быть возвращен.

Стюарт Маркс
источник
26
Похоже, что flatMapоперация является полной противоположностью плоской. Еще раз, оставьте это для Компьютерных Ученых, чтобы перевернуть термин на его голову. Как функция, являющаяся «прозрачной», то есть вы не можете видеть ничего, что она делает, только результаты, в то время как в разговорной речи вы говорите, что хотите, чтобы процесс был прозрачным, означает, что вы хотите видеть каждую его часть.
coladict
45
@coladict Попробуйте взглянуть на это с другой точки зрения: это не прозрачный случай, когда вы можете видеть внутреннюю работу, но сама функция прозрачна, то есть невидима для вас, все еще выполняя свою работу и позволяя вам увидеть то, что вы ' работает с В этом случае «плоский» относится к противоположности «вложенному», а flatmap удаляет один уровень вложенности путем выравнивания.
Zefiro
7
@coladict «Прозрачная» штука пожирает мою голову годами. Рад, что хотя бы один человек чувствует то же самое.
Ашок Биджой Дебнатх
9
Уплощение происходит от превращения двухуровневой структуры в одноуровневую структуру, см. Ответ Dici для примера stackoverflow.com/a/26684582/6012102
andrzej.szmukala
26
Это лучшее объяснение flatMap . Это то, что заставляет все это щелкать: значения из потока, возвращаемого преобразователем, выводятся из потока и передаются в выходной поток. «Скопления» значений, возвращаемых при каждом вызове функции преобразования, вообще не различаются в выходном потоке, поэтому говорят, что вывод «сглажен» . Спасибо!
neevek
465

Stream.flatMapКак можно догадаться по его названию, это сочетание a mapи flatоперации. Это означает, что вы сначала применяете функцию к своим элементам, а затем выравниваете ее. Stream.mapтолько применяет функцию к потоку без выравнивания потока.

Чтобы понять, в чем состоит уплощение потока, рассмотрим такую ​​структуру, [ [1,2,3],[4,5,6],[7,8,9] ]которая имеет «два уровня». Уплощение означает преобразование в структуру «одного уровня» [ 1,2,3,4,5,6,7,8,9 ].

Dici
источник
6
простой и сладкий
bluelurker
3
Хаха, если честно, я все еще удивлен, увидев, сколько трафика получает этот вопрос. Другое забавное наблюдение состоит в том, что уже почти 5 лет я писал этот ответ, и была довольно последовательная схема голосования, когда принятый ответ получает примерно два отзыва за каждый ответ. Это удивительно последовательно.
Дичи
1
как это не принятый ответ, спасибо за то, что
пришли
234

Я хотел бы привести 2 примера, чтобы получить более практическую точку зрения:
Первый пример, использующий карту:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}   

Ничего особенного в первом примере, a Functionприменяется для возврата Stringв верхний регистр.

Второй пример, использующий flatMap:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

Во втором примере передается поток списка. Это не поток целых чисел!
Если необходимо использовать функцию преобразования (через карту), то сначала поток должен быть сведен к чему-то другому (поток целых чисел).
Если flatMap удален, то возвращается следующая ошибка: Оператор + не определен для типа (ов) аргумента List, int.
Нельзя применить + 1 к списку целых чисел!

Руди Визерс
источник
@PrashanthDebbadwar Я думаю, что в итоге вы получите поток, Stream<Integer>а не поток Integer.
Пейн
166

Пожалуйста, пройдите по почте полностью, чтобы получить ясное представление,

карта против плоской карты:

Чтобы вернуть длину каждого слова из списка, мы сделаем что-то вроде ниже.

Короткая версия приведена ниже

Когда мы собираем два списка, приведенных ниже

Без плоской карты => [1,2], [1,1] => [[1,2], [1,1]] Здесь два списка размещены внутри списка, поэтому на выходе будет список, содержащий списки

С плоской картой => [1,2], [1,1] => [1,2,1,1] Здесь два списка сглаживаются и в список помещаются только значения, поэтому на выходе будет список, содержащий только элементы

В основном это объединяет все объекты в один

## Подробная версия была дана ниже: -

Например: -
Рассмотрим список [«STACK», «OOOVVVER»], и мы пытаемся вернуть список наподобие [«STACKOVER»] (возвращая только уникальные буквы из этого списка). Сначала мы бы сделали что-то подобное ниже, чтобы вернуть список [«STACKOVER»] из [«STACK», «ОООВВВЕР»]

public class WordMap {
  public static void main(String[] args) {
    List<String> lst = Arrays.asList("STACK","OOOVER");
    lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
  }
}

Здесь проблема в том, что лямбда, переданная методу карты, возвращает массив String для каждого слова, поэтому поток, возвращаемый методом карты, на самом деле имеет тип Stream, но нам нужен Stream для представления потока символов, ниже изображение иллюстрирует проблема.

Рисунок А:

введите описание изображения здесь

Вы можете подумать, что мы можем решить эту проблему, используя flatmap,
хорошо, давайте посмотрим, как решить эту проблему, используя map и Arrays.stream. Прежде всего вам понадобится поток символов вместо потока массивов. Существует метод с именем Arrays.stream (), который принимает массив и создает поток, например:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .map(Arrays::stream).distinct() //Make array in to separate stream
    .collect(Collectors.toList());

Выше все равно не работает, потому что теперь мы получаем список потоков (точнее, Stream>). Вместо этого мы должны сначала преобразовать каждое слово в массив отдельных букв, а затем превратить каждый массив в отдельный поток.

Используя flatMap, мы сможем решить эту проблему, как показано ниже:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
    .collect(Collectors.toList());

flatMap будет выполнять сопоставление каждого массива не с потоком, а с содержимым этого потока. Все отдельные потоки, которые генерируются при использовании map (Arrays :: stream), объединяются в один поток. На рисунке B показан эффект использования метода flatMap. Сравните это с тем, что делает карта на рисунке A. Рисунок B введите описание изображения здесь

Метод flatMap позволяет заменить каждое значение потока другим потоком, а затем объединяет все созданные потоки в один поток.

TechDog
источник
2
Хорошее схематичное объяснение.
Hitesh
108

Ответ в одной строке: flatMapпомогает сплющить Collection<Collection<T>>вCollection<T> . Таким же образом, это также сгладит Optional<Optional<T>>в Optional<T>.

введите описание изображения здесь

Как видите, map()только с:

  • Промежуточный тип Stream<List<Item>>
  • Тип возврата List<List<Item>>

и с flatMap():

  • Промежуточный тип Stream<Item>
  • Тип возврата List<Item>

Это результат теста из кода, использованного прямо ниже:

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [Laptop, Phone, Mouse, Keyboard]

Используемый код :

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class Parcel {
  String name;
  List<String> items;

  public Parcel(String name, String... items) {
    this.name = name;
    this.items = Arrays.asList(items);
  }

  public List<String> getItems() {
    return items;
  }

  public static void main(String[] args) {
    Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
    Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
    List<Parcel> parcels = Arrays.asList(amazon, ebay);

    System.out.println("-------- Without flatMap() ---------------------------");
    List<List<String>> mapReturn = parcels.stream()
      .map(Parcel::getItems)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + mapReturn);

    System.out.println("\n-------- With flatMap() ------------------------------");
    List<String> flatMapReturn = parcels.stream()
      .map(Parcel::getItems)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + flatMapReturn);
  }
}
nxhoaf
источник
8
Очень четкий пример .., не потребовалось бы больше нескольких секунд, чтобы понять концепцию на вашем примере ...
TechDog
2
приятное объяснение. действительно ценю за простое и лучшее объяснение
Sachin Rane
42

Функция, которую вы передаете stream.map , должна возвращать один объект. Это означает, что каждый объект во входном потоке приводит к точно одному объекту в выходном потоке.

Функция, которую вы передаете, stream.flatMapвозвращает поток для каждого объекта. Это означает, что функция может возвращать любое количество объектов для каждого входного объекта (включая ни одного). Результирующие потоки затем объединяются в один выходной поток.

Philipp
источник
Почему вы хотите «вернуть любое количество объектов для каждого входного объекта (включая ни одного)»?
Дерек Махар
4
@DerekMahar Было бы много вариантов использования для этого. Например, предположим, у вас есть поток Departments в вашей организации. Каждый отдел имеет от 0 до n Employeeс. Что вам нужно, это поток всех сотрудников. Ну так что ты делаешь? Вы пишете метод flatMap, который принимает отдел и возвращает поток его сотрудников.
Филипп
Филипп, ваш пример иллюстрирует основную причину использования flatMap? Я подозреваю, что это может быть случайным и не иллюстрирует ключевой вариант использования или причину, по которой он flatMapсуществует. (Продолжение ниже ...)
Дерек Махар
После прочтения dzone.com/articles/understanding-flatmap я думаю, что основной мотивацией flatMapявляется устранение ошибок, которые могут возникнуть при использовании map. Как вы обрабатываете случаи, когда один или несколько элементов в исходном наборе не могут быть сопоставлены с выходным элементом? Введение промежуточного набора (скажем, Optionalили Stream) для каждого объекта ввода flatMapпозволяет исключить «недопустимые» объекты ввода (или так называемые «плохие яблоки» в духе stackoverflow.com/a/52248643/107158 ) из финальный сет.
Дерек Махар
1
@DerekMahar Да, элементы, в которых каждый входной объект может возвращать или не возвращать выходной объект, - еще один хороший пример использования плоской карты.
Филипп
29

для карты у нас есть список элементов и (функция, действие) f так:

[a,b,c] f(x) => [f(a),f(b),f(c)]

и для плоской карты у нас есть список элементов списка, и у нас есть (функция, действие) f, и мы хотим, чтобы результат был сведен:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
Бачири Тауфик Абдеррахман
источник
25

У меня такое ощущение, что большинство ответов здесь усложняют простую проблему. Если вы уже понимаете, как mapработает, это должно быть довольно легко понять.

Есть случаи, когда мы можем получить нежелательные вложенные структуры при использовании map(), flatMap()метод предназначен для преодоления этого путем избежания переноса.


Примеры:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

Мы можем избежать использования вложенных списков, используя flatMap:

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

где:

private Optional<String> findById(Integer id)
Гжегож Пивоварек
источник
извините, но второй фрагмент из пункта 1 не компилируется вместо List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. Это должно бытьStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Артур
@arthur Я думаю, что я использовал Vavr Stream и List здесь - но я согласен, что это может немного сбить с толку - я изменю это на стандартную Java
Grzegorz Piwowarek
22

В статье Oracle, посвященной Optional, подчеркивается это различие между картой и плоской картой:

String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

К сожалению, этот код не компилируется. Почему? Переменная computer имеет тип Optional<Computer>, поэтому совершенно правильно вызывать метод map. Однако getSoundcard () возвращает объект типа Optional. Это означает, что результатом операции map является объект типаOptional<Optional<Soundcard>> . В результате вызов getUSB () является недопустимым, поскольку внешний Optional содержит в качестве значения другой Optional, который, конечно, не поддерживает метод getUSB ().

В случае потоков метод flatMap принимает функцию в качестве аргумента, которая возвращает другой поток. Эта функция применяется к каждому элементу потока, в результате чего создается поток потоков. Однако flatMap заменяет каждый сгенерированный поток содержимым этого потока. Другими словами, все отдельные потоки, которые генерируются функцией, объединяются или «сплющиваются» в один отдельный поток. Здесь мы хотим что-то похожее, но мы хотим «свести» двухуровневый факультативный в один .

Необязательно также поддерживает метод flatMap. Его цель - применить функцию преобразования к значению Optional (точно так же, как это делает операция map), а затем объединить получившийся двухуровневый Optional в один .

Итак, чтобы сделать наш код корректным, нам нужно переписать его следующим образом, используя flatMap:

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

Первый flatMap гарантирует, что Optional<Soundcard>возвращается вместо вместо Optional<Optional<Soundcard>>, а второй flatMap достигает той же цели для возврата Optional<USB>. Обратите внимание, что третий вызов просто должен быть map (), потому что getVersion () возвращает String, а не необязательный объект.

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

Расти ядро
источник
1
вопрос был о Stream.map и Stream.flatMap, а не о Optional.map и
Anfd
4
Но это очень помогло мне понять мои проблемы с опциональной и плоской картой, большое спасибо!
Лоик
2
@djames, это совершенно правильный ответ, прочитайте его, начиная с параграфа «С потоками метод flatMap принимает функцию в качестве аргумента ...» :)
skwisgaar
Я думаю, что это очень полезное дополнение к некоторым другим ответам здесь.
Mz A
Версия flatMap () также создает исключение nullpointerexception, если soundCard имеет значение null. Так где же обещанная выгода от Optional?
Екатерина
16

Я не уверен, что должен ответить на этот вопрос, но каждый раз, когда я сталкиваюсь с кем-то, кто этого не понимает, я использую один и тот же пример.

Представь, что у тебя есть яблоко. А mapпревращает это яблоко, apple-juiceнапример, или в однозначное сопоставление.

Возьмите то же самое яблоко и достаньте из него только семена, то есть то flatMap, что нужно, или одно ко многим , одно яблоко как вход, много семян как выход.

Евгений
источник
4
Это интересный пример :)
Кассиомолин
Для flatMapслучая, вы сначала собираете семена каждого яблока в отдельные пакеты, по одному пакету на яблоко, прежде чем выливать все пакеты в один пакет?
Дерек Махар
@DerekMahar до java-10 раньше он был бедным в одной сумке, то есть на flatmapсамом деле не ленивый, но с java-10 ленивый
Евгений
@ Евгений, пожалуйста, объясни мне немного более ленивую концепцию, которую ты пытаешься объяснить, неясную для меня. Я понял, что derkerMahar объяснил в комментарии, это то, что происходило до java10?
JAVA
@JAVA просто ищи flatMap + lazy, держу пари, будут ответы.
Евгений
16

map () и flatMap ()

  1. map()

Просто принимает Function лямбда-параметр, где T - элемент, а R - возвращаемый элемент, построенный с использованием T. В конце мы получим Stream с объектами типа R. Простой пример может быть:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

Он просто берет элементы с 1 по 5 типа Integer, использует каждый элемент для создания нового элемента из типа Stringсо значением "prefix_"+integer_valueи печатает его.

  1. flatMap()

Полезно знать, что flatMap () принимает функцию, в F<T, R>которой

  • T - это тип, из которого можно построить поток из / с . Это может быть List (T.stream ()), массив (Arrays.stream (someArray)) и т. Д. Все, из чего может быть получен поток в / или форме. в приведенном ниже примере у каждого разработчика много языков, поэтому dev. Languages ​​- это список и будет использовать лямбда-параметр.

  • R - это результирующий поток, который будет построен с использованием T. Зная, что у нас есть много экземпляров T, у нас, естественно, будет много потоков из R. Все эти потоки из типа R теперь будут объединены в один «плоский» поток из типа R ,

пример

Примеры Бачири Тауфика, его ответ здесь прост и понятен. Просто для ясности, скажем так, у нас есть команда разработчиков:

dev_team = {dev_1,dev_2,dev_3}

каждый разработчик знает много языков:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}

Применение Stream.map () к dev_team для получения языков каждого разработчика:

dev_team.map(dev -> dev.getLanguages())

даст вам эту структуру:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

который в основном List<List<Languages>> /Object[Languages[]]. Не очень красиво и не похоже на Java8!

с помощью Stream.flatMap()него можно «сгладить» вещи, поскольку он берет вышеупомянутую структуру
и превращает ее в {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, что в основном может использоваться как List<Languages>/Language[]/etc...

так что в конце ваш код будет иметь больше смысла, как это:

dev_team
   .stream()    /* {dev_1,dev_2,dev_3} */
   .map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
   .flatMap(languages ->  languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
   .doWhateverWithYourNewStreamHere();

или просто:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

Когда использовать map () и flatMap () :

  • Используется, map()когда каждый элемент типа T из вашего потока должен отображаться / преобразовываться в один элемент типа R. Результатом является отображение типа (1 начальный элемент -> 1 конечный элемент) и новый поток элементов типа R возвращается

  • Используйте, flatMap()когда каждый элемент типа T из вашего потока должен отображаться / преобразовываться в Коллекции элементов типа R. Результатом является отображение типа (1 начальный элемент -> n конечных элементов) . Эти коллекции затем объединяются (или сплющиваются ) с новым потоком элементов типа R. Это полезно, например, для представления вложенных циклов .

Pre Java 8:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

Post Java 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));
Артур
источник
11

Карта: - Этот метод принимает одну функцию в качестве аргумента и возвращает новый поток, состоящий из результатов, сгенерированных путем применения переданной функции ко всем элементам потока.

Давайте представим, у меня есть список целочисленных значений (1,2,3,4,5) и один интерфейс функции, логика которого является квадратом переданного целого числа. (е -> е * е).

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

вывод:-

[1, 4, 9, 16, 25]

Как видите, выход - это новый поток, значения которого являются квадратами значений входного потока.

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

FlatMap: - Этот метод принимает одну функцию в качестве аргумента, эта функция принимает один параметр T в качестве входного аргумента и возвращает один поток параметра R в качестве возвращаемого значения. Когда эта функция применяется к каждому элементу этого потока, она создает поток новых значений. Все элементы этих новых потоков, сгенерированных каждым элементом, затем копируются в новый поток, который будет возвращаемым значением этого метода.

Давайте представим, у меня есть список предметов учеников, где каждый ученик может выбрать несколько предметов.

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

вывод:-

[economics, biology, geography, science, history, math]

Как вы можете видеть, вывод - это новый поток, значения которого представляют собой совокупность всех элементов потоков, возвращаемых каждым элементом входного потока.

[S1, S2, S3] -> [{"история", "математика", "география"}, {"экономика", "биология"}, {"наука", "математика"}] -> принять уникальные предметы - > [экономика, биология, география, наука, история, математика]

http://codedestine.com/java-8-stream-flatmap-method/

lalitbhagtani
источник
может иметь значение, если вы предоставите код вместо предоставления ссылки на документ
Чарльз-Антуан
11

.map для отображения A -> B

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

он преобразует любой элемент Aв любой элемент B. Javadoc


.flatMap для А -> Поток <B> concatinating

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

он --1 преобразует любой элемент Aв Stream< B>, а затем --2 объединяет все потоки в один (плоский) поток. Javadoc


Примечание 1: Хотя последний пример представляет собой поток примитивов (IntStream) вместо потока объектов (Stream), он все же иллюстрирует идею .flatMap.

Примечание 2. Несмотря на имя, метод String.chars () возвращает целые числа. Таким образом, фактическая коллекция будет: [100, 111, 103, 99, 97, 116] где 100код 'd', 111код 'o'и т. Д. Опять же, в иллюстративных целях, она представлена ​​как [d, o, g, c, a, t].

EPoX
источник
3
Лучший ответ. Прямо к
делу
1

Простой ответ

mapОперация может производить Streamиз Stream.EXStream<Stream<Integer>>

flatMapоперация будет только производить Streamчто-то. EXStream<Integer>

Мелад Василиус
источник
0

Также хорошая аналогия может быть с C #, если вы знакомы с. В основном C # Selectпохож на Java mapи C # SelectManyJava flatMap. То же относится и к Kotlin для коллекций.

Арсениус
источник
0

Это очень запутанно для начинающих. Основное различие заключается в mapвыделении одного элемента для каждой записи в списке и flatMapв основном операции map+ flatten. Чтобы быть более понятным, используйте flatMap, когда вам требуется более одного значения, например, когда вы ожидаете, что цикл возвратит массивы, flatMap будет действительно полезен в этом случае.

Я написал блог об этом, вы можете проверить это здесь .

Нирадж Чаухан
источник
0

Поток операций flatMapи mapпринять функцию в качестве ввода.

flatMapожидает, что функция возвратит новый поток для каждого элемента потока и возвратит поток, который объединяет все элементы потоков, возвращаемых функцией для каждого элемента. Другими словами, flatMapдля каждого элемента из источника функция создаст несколько элементов. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapожидает, что функция вернет преобразованное значение и вернет новый поток, содержащий преобразованные элементы. Другими словами, mapдля каждого элемента из источника функция создаст один преобразованный элемент. http://www.zoftino.com/java-stream-examples#map-operation

Арнав Рао
источник
0

flatMap()также использует частичную ленивую оценку потоков. Он будет читать первый поток и только при необходимости перейдет к следующему потоку. Поведение подробно объясняется здесь: Гарантируется ли flatMap ленивость?

SK
источник
0

Если вы думаете map()как итерация (одноуровневый forцикл), flatmap()это двухуровневая итерация (как вложенный forцикл). (Введите каждый повторяющийся элемент foo, сделайте foo.getBarList()и barListповторите это снова)


map(): взять поток, сделать что-то для каждого элемента, собрать единственный результат каждого процесса, вывести другой поток. Определение «сделать что-то функция» неявно. Если обработка какого-либо элемента приводит к null, nullиспользуется для создания окончательного потока. Итак, количество элементов в результирующем потоке будет равно номеру входного потока.

flatmap(): взять поток элементов / потоков и функцию (явное определение), применить функцию к каждому элементу каждого потока и собрать весь промежуточный результирующий поток, чтобы получить больший поток («выравнивание»). Если обработка какого-либо элемента приводит к тому null, что пустой поток передается на последний этап «выравнивания». Количество элементов в результирующем потоке является суммой всех участвующих элементов во всех входных данных, если на входе находится несколько потоков.

WesternGun
источник