Яка різниця між методами map () та flatMap () в Java 8?


705

У Java 8, в чому різниця між методами Stream.map()та Stream.flatMap()методами?


55
Підпис типу ніби розповідає всю історію. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Кріс Мартін

97
fwiw, ці підписи типу навіть не схожі на Java. (Я знаю, я знаю , - але сказати , що це говорить «вся історія» WRT карти / flatMap передбачає багато знань про новий і поліпшеному «Java ++»)
Майкла

16
@michael Підпис цього типу схожий на Haskell, а не на Java. Але це не ясно , чи є який - або більш читабельним фактична Java підпис: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Стюарт Маркс

7
Ха, так, я мав на увазі "фактичну Яву". Як і C ++, сучасна Java майже не впізнавана для тих, хто почав її використовувати в 90-х (як я, обидві мови). Лише відповідаючи на коментар, що підписи методів навряд чи розповідають "цілу історію", принаймні не більше, не без додаткового викладу (або в цьому випадку коментарів, перекладу).
Майкл

2
Який повинен сказати, map«и картографа лямбда повертає R, A flatMap» и картографа лямбда повертає Streamз R( Stream<R>). Потоки, повернуті flatMapкартографом, ефективно з'єднані. В іншому випадку і обидва, mapі flatMapповернення Stream<R>; різниця , що повертається картограф лямбда, Rпроти Stream<R>.
дерекем

Відповіді:


814

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

Це відображено в аргументах до кожної операції.

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

flatMapОперація приймає функцію , яка концептуально хоче споживати одне значення і зробити довільне число значень. Однак у Java метод, який може повернути довільну кількість значень, громіздкий, оскільки методи можуть повернути лише нуль або одне значення. Можна уявити API, де функція картографування для flatMapприймає значення і повертає масив або aListзначень, які потім надсилаються на вихід. Зважаючи на те, що це бібліотека потоків, особливим правильним способом подання довільної кількості повернутих значень є те, що функція відображення самої картки повертає потік! Значення потоку, повернутого картографом, виводяться з потоку і передаються у вихідний потік. "Згустки" значень, що повертаються кожним викликом до функції відображення, у вихідному потоці взагалі не відрізняються, тому, як кажуть, вихід "згладжений".

Зазвичай використовується для функції картографа з flatMapповернення , Stream.empty()якщо він хоче послати нульові значення, або що - щось подібне , Stream.of(a, b, c)якщо він хоче повернути кілька значень. Але звичайно будь-який потік можна повернути.


26
Мені здається, що flatMapоперація є прямо протилежною плоскості. Знову ж таки, залиште це комп'ютерним вченим, щоб перетворити термін на голову. Наче функція є "прозорою", тобто ви не бачите нічого, що вона робить, лише результати, в той час як розмовно кажучи, що ви хочете, щоб процес був прозорим, означає, що ви хочете, щоб кожна його частина була помічена.
coladict

44
@coladict Спробуйте переглядати це з іншого погляду: це не прозорий випадок, коли ви можете бачити внутрішню роботу, але сама функція для вас прозора, тобто невидима - поки ви все ще робите свою роботу і дозволяєте вам бачити те, що ви ' повторно працюємо. У цьому випадку "плоскість" відноситься до протилежної "вкладеної", плоска карта видаляє один рівень гніздування шляхом вирівнювання.
Зефіро

7
@coladict "Прозора" річ їла мені голову роками. Радий знати, що принаймні одна людина відчуває те саме.
Ashok Bijoy Debnath

9
Зведення приходить від повороту структури 2 рівня в структуру одного рівня, см відповіді Dici для прикладу stackoverflow.com/a/26684582/6012102
andrzej.szmukala

26
Це найкраще пояснення FlatMap . Це те, що робить все це клацанням: значення з потоку, повернутого картографом, виводяться з потоку і передаються у вихідний потік. "Згустки" значень, що повертаються кожним викликом до функції відображення, у вихідному потоці взагалі не розрізняються, тому, як кажуть, вихід "згладжений" . Дякую!
neevek

464

Stream.flatMap, як можна здогадатися за назвою, - це поєднання операції mapта flatоперації. Це означає, що ви спочатку застосуєте функцію до своїх елементів, а потім розрівняєте її. Stream.mapзастосовує лише функцію до потоку без згладжування потоку.

Щоб зрозуміти, з чого складається згладжування потоку, розглянемо структуру на зразок, [ [1,2,3],[4,5,6],[7,8,9] ]яка має "два рівні". Зведення це означає , перетворюючи його в структурі «один рівень»: [ 1,2,3,4,5,6,7,8,9 ].


5
simple and sweet
bluelurker

3
Ха-ха, чесно кажучи, я все ще здивований, бачачи, скільки трафіку отримує це питання. Ще одне кумедне зауваження полягає в тому, що вже майже 5 років я написав цю відповідь, і існує досить послідовна модель обґрунтування, коли прийнята відповідь отримує приблизно два оновлення за кожну мою відповідь. Це напрочуд послідовно.
Dici

1
як це не прийнята відповідь, дякую за те, що я
перейшов

233

Я хотів би навести 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);
}   

У першому прикладі нічого особливого 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);
}

У другому прикладі передається Потік списку. Це НЕ потік цілості!
Якщо потрібно використовувати функцію перетворення (через карту), то спочатку Потік повинен бути прирівняний до чогось іншого (Stream of Integer).
Якщо видалено flatMap, повертається така помилка: Оператор + не визначено для списку типів аргументів, списку, int.
НЕ можна застосовувати +1 у списку цілих чисел!


@PrashanthDebbadwar Я думаю, ви б закінчилися Потоком, Stream<Integer>а не Потоком Integer.
Payne

166

Будь ласка, перейдіть до публікації повністю, щоб отримати чітке уявлення,

карта проти flatMap:

Щоб повернути довжину кожного слова зі списку, ми зробимо щось подібне нижче.

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

Коли ми збираємо два списки, наведені нижче

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

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

В основному він об'єднує всі об'єкти в один

## Детальна версія надана нижче: -

Наприклад: -
Розгляньте список [“STACK”, “OOOVVVER”], і ми намагаємось повернути такий список, як [“STACKOVER”] (повертаючи з цього списку лише унікальні літери). Спочатку ми зробимо щось подібне нижче, щоб повернути список [“STACKOVER”] від [“STACK”, “OOOVVVER”]

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());
  }
}

Тут проблема полягає в тому, що Ламбда, переданий методу map, повертає масив String для кожного слова. Отже, потік, повернутий методом map, насправді має тип Stream, але нам потрібен Stream для представлення потоку символів, нижче зображення зображено проблема.

Малюнок A:

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

Ви можете подумати, що ми можемо вирішити цю проблему за допомогою плоскої карти.
Добре, давайте подивимося, як вирішити це за допомогою 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 буде виконувати відображення кожного масиву не з потоком, а з вмістом цього потоку. Усі окремі потоки, які будуть генеровані під час використання карти (масиви :: потік), об'єднуються в один потік. На малюнку B показано ефект використання методу flatMap. Порівняйте його з картою, яку робить на малюнку А. Малюнок B введіть тут опис зображення

Метод flatMap дозволяє замінити кожне значення потоку на інший потік, а потім об'єднає всі створені потоки в один потік.


2
Гарне схематичне пояснення.
Hitesh

107

Відповідь в одному рядку: flatMapдопомагає згладити Collection<Collection<T>>aCollection<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);
  }
}

8
Дуже чіткий приклад .., Щоб розібратися в концепції з вашим прикладом, не знадобиться більше декількох секунд ...
TechDog

2
приємне пояснення. дуже вдячний за просте і найкраще пояснення
Sachin Rane

42

Функція, яку ви передаєте stream.map , повинна повертати один об'єкт. Це означає, що кожен об'єкт у вхідному потоці призводить до отримання точно одного об'єкта у вихідному потоці.

Функція, яку ви передаєте, stream.flatMapповертає потік для кожного об'єкта. Це означає, що функція може повертати будь-яку кількість об'єктів для кожного вхідного об'єкта (включаючи жодного). Потім отримані потоки об'єднуються в один вихідний потік.


Чому ви хочете "повернути будь-яку кількість об'єктів для кожного вхідного об'єкта (включаючи жодного)"?
Дерек Махар

4
@DerekMahar Для цього було б багато випадків використання. Наприклад, скажімо, у вас є потік Departments у вашій організації. Кожен відділ має від 0 до n Employees. Що вам потрібно - це потік усіх працівників. Так, що ти робиш? Ви пишете метод 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 Я думаю, що тут я використав "Потік і список Вавра", але я згоден, що це може трохи заплутати - я зміню це на стандартну Java
Grzegorz Piwowarek


22

Стаття Oracle про Додатковий підкреслює цю різницю між картою та плоскою картою:

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

На жаль, цей код не компілюється. Чому? Змінний комп'ютер типу Optional<Computer>, тому цілком коректно називати метод карт. Однак getSoundcard () повертає об'єкт типу Необов’язково. Це означає, що результат операції з картою є об’єктом типуOptional<Optional<Soundcard>> . Як результат, виклик getUSB () є недійсним, оскільки найвіддаленіший необов'язковий містить як своє значення інший необов'язковий, який, звичайно, не підтримує метод getUSB ().

З потоками метод flatMap приймає функцію як аргумент, який повертає інший потік. Ця функція застосовується до кожного елемента потоку, що призведе до потоку потоків. Однак flatMap має наслідком заміщення кожного створеного потоку вмістом цього потоку. Іншими словами, всі окремі потоки, що генеруються функцією, об'єднуються або «сплющуються» в один єдиний потік. Ми хочемо тут щось подібне, але ми хочемо «вирівняти» дворівневий факультативний в один .

Необов’язково також підтримує метод flatMap. Його мета - застосувати функцію перетворення на значення необов'язкового (як і операція з картою), а потім вирівняти отриманий дворівневий необов'язковий елемент в єдине .

Отже, щоб зробити наш код правильним, нам потрібно переписати його наступним чином, використовуючи flatMap:

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

Перший flatMap забезпечує Optional<Soundcard>повернення an замість an Optional<Optional<Soundcard>>, а другий flatMap досягає тієї ж мети, щоб повернути an Optional<USB>. Зауважте, що третім викликом просто має бути карта (), оскільки getVersion () повертає String, а не необов'язковий об'єкт.

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


1
питання стосувалося Stream.map та Stream.flatMap & не про Optional.map anfd Optional.flatMap
djames

4
Але мені це дуже допомогло зрозуміти мої проблеми з факультативною та плоскою картою, велике спасибі!
Loïc

2
@djames, це абсолютно правильна відповідь, прочитайте її, починаючи з параграфа "З потоками метод flatMap приймає функцію як аргумент ..." :)
skwisgaar

Я думаю, що це дуже корисне доповнення до деяких інших відповідей тут.
Мз А

Версія flatMap () також викидає nullpointerexception, якщо soundCard є null. То де ж обіцяна вигода Факультативу?
Катерина

16

Я не дуже впевнений, що повинен відповісти на це, але кожен раз, коли я стикаюся з кимось, хто цього не розуміє, я використовую той самий приклад.

Уявіть, у вас є яблуко. A mapперетворює це яблуко в apple-juiceнаприклад, або відображення " один на один" .

Візьміть те саме яблуко і дістайте з нього лише насіння, ось що flatMap робить, або одне для багатьох , одне яблуко як вхідне, багато насіння як вихід.


4
Ось цікавий приклад :)
Кассіомолін

Для flatMapвипадку, ви спочатку збираєте насіння з кожного яблука в окремі пакетики, по одному пакетику на яблуко, перш ніж висипати всі пакетики в один мішок?
Дерек Махар

@DerekMahar він був бідним в єдиний мішок перед java-10, це означає, що flatmapнасправді не лінивий, але оскільки java-10 він лінивий
Євген

@Eugene, поясніть, будь ласка, трохи більш ледачу концепцію, яку ви намагаєтесь пояснити, не зрозуміло для мене. Я зрозумів, що derkerMahar пояснив у коментарі, це те, що відбувається перед java10?
JAVA

@JAVA просто шукаю flatMap + lazy, я думаю, що відповіді знайдуться.
Євген

16

map () та flatMap ()

  1. map()

Просто приймає функцію лямбда-парам, де 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 - тип, з якого може бути побудований Потік з / з . Це може бути список (T.stream ()), масив (Arrays.stream (someArray)) тощо. Будь-що, з чого може бути потік з / або формуватися. у прикладі нижче кожного розробника є багато мов, тому dev. Мови - це Список і використовуватиме параметр лямбда.

  • 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();

Коли використовувати карту () та використовувати flatMap () :

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

  • Використовуйте, flatMap()коли кожен елемент типу T зі свого потоку повинен бути відображений / перетворений на Збірник елементів типу R. Результатом є відображення типу (1 початковий елемент -> n кінцевих елементів) . Ці колекції потім об'єднуються (або сплющуються ) до нового потоку елементів типу R. Це корисно, наприклад, для представлення вкладених циклів .

Попередньо Java 8:

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

Опублікувати 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/


може змінити значення, якщо ви надасте код замість того, щоб просто продовжувати посилання на doc
Чарльз-Антуан Фурнел,

11

.map для А -> B відображення

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

він перетворює будь-який предмет Aна будь-який предмет B. Javadoc


.flatMap призначений для A -> Stream <B> conatitate

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].


3
Найкраща відповідь. Прямо до
суті

1

Проста відповідь.

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

flatMapОперація буде виробляти лише Streamщось. EXStream<Integer>


0

Також хороша аналогія може бути з C #, якщо ви знайомі. В основному C # Selectсхожий на java mapта C # SelectManyjava flatMap. Те ж стосується і Котліна для колекцій.


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

Якщо ви розглядаєте map()як ітерацію ( forцикл одного рівня ), flatmap()це дворівнева ітерація (як вкладена forпетля). (Введіть кожен ітераційний елемент fooі зробіть foo.getBarList()і barListповторіть його ще раз)


map(): взяти потік, зробити щось кожному елементу, зібрати єдиний результат кожного процесу, вивести інший потік. Визначення "робити щось функціонування" неявне. Якщо обробка будь-якого елемента призводить до null,null використовується для складання остаточного потоку. Отже, кількість елементів у отриманому потоці буде дорівнює кількості вхідного потоку.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.