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

В Java 8, какова разница между Stream.map и Stream.flatMap ?

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

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

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

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

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

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

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

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

 @Test public void convertStringToUpperCaseStreams() { List 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 together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List .flatMap(List::stream) .map(integer -> integer + 1) .collect(Collectors.toList()); assertEquals(asList(2, 3, 4, 5), together); } 

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

Пожалуйста, прочитайте сообщение полностью, чтобы получить четкое представление, map vs flatMap: чтобы вернуть длину каждого слова из списка, мы сделаем что-то вроде ниже ..

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

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

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

Рисунок A:

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

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

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

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

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

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

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

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

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

для Карты мы имеем список элементов и (функцию, действие) 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)] 

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

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


Примеры:

1

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

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

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

2

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

где:

 private Optional findById(Integer id) 

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

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

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

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

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

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

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

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

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

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

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

 List intList = Arrays.asList(1, 2, 3, 4, 5); List newList = intList.stream().map( e -> e * e ).collect(Collectors.toList()); System.out.println(newList); 

вывод:-

 [1, 4, 9, 16, 25] 

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

 [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 в качестве входного аргумента и возвращает один stream параметра R в качестве возвращаемого значения. Когда эта функция применяется к каждому элементу этого streamа, она создает stream новых значений. Все элементы этих новых streamов, генерируемые каждым элементом, затем копируются в новый stream, который будет возвратным значением этого метода.

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

 List studentList = new ArrayList(); 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 courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet()); System.out.println(courses); 

вывод:-

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

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

[S1, S2, S3] -> [{«история», «математика», «география»}, {«экономика», «биология»}, {«наука», «математика»}] – > [экономика, биология, география, наука, история, математика]

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

Один ответ строки: flatMap помогает сгладить Collection> в Collection .

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

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

 -------- Without flatMap() ------------------------------- collect return: [[Laptop, Phone], [Mouse, Keyboard]] -------- With flatMap() ---------------------------------- collect return: [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 items; public Parcel(String name, String... items) { this.name = name; this.items = Arrays.asList(items); } public List getItems() { return items; } public static void main(String[] args) { Parcel amazon = new Parcel("amazon", "Laptop", "Phone"); Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard"); List parcels = Arrays.asList(amazon, ebay); System.out.println("-------- Without flatMap() -------------------------------"); List> mapReturn = parcels.stream() .map(Parcel::getItems) .collect(Collectors.toList()); System.out.println("\t collect return: " + mapReturn); System.out.println("\n-------- With flatMap() ----------------------------------"); List flatMapReturn = parcels.stream() .map(Parcel::getItems) .flatMap(Collection::stream) .collect(Collectors.toList()); System.out.println("\t collect return: " + flatMapReturn); } } 

map () и flatMap ()

  1. map()

Просто берет функцию lambda-парама, где 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()

Полезно знать, что flapMap () принимает функцию F где

  • T – тип, из которого Stream может быть построен из / с . Это может быть List (T.stream ()), массив (Arrayys.stream (someArray)) и т. Д. Все, из которого Stream может быть с / или формой. в приведенном ниже примере каждый разработчик имеет много языков, поэтому dev. Языки – это список и будет использовать параметр lambda.

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

Примеры Bachiri Taoufiq см. В его ответе здесь просты и понятны. Просто для ясности, позвольте сказать, что у нас есть команда разработчиков:

 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> /Object[Languages[]] . Не так красиво, ни Java8-like !!

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

поэтому конец вашего кода будет иметь большее значение:

 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 из вашего streamа должен быть отображен / преобразован в один элемент типа R. Результатом является отображение типа (1 начальный элемент -> 1 конечный элемент) и новый stream элементов типа R.

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

Предварительная Java 8:

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

Сообщение Java 8

 myFoos .stream() .flat(foo -> foo.getMyBars().stream()) .forEach(bar -> System.out.println(bar.getMyName())); 

Также хорошая аналогия может быть с C #, если вы знакомы. В основном C # Select аналогичную Java- map и C # SelectMany java flatMap . То же самое относится к Котлину для коллекций.

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

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

Различие становится ясным, если взять простой пример:

 List> matrix = List.of(List.of(1, 2), List.of(3, 4)); 

objective: напечатать все значения матрицы (каким-либо образом).

flatMap

Возьмем Function которая принимает один аргумент и создает Stream :

 Function, Stream> fn = list -> list.stream(); 

И примените эту функцию к каждому элементу (списку) в матрице:

 for (List list : matrix) { Stream stream = fn.apply(list); stream.forEach(System.out::print); } 

Это именно то, что может сделать Stream.flatMap(fn) в сжатом виде:

 matrix.stream().flatMap(fn).forEach(System.out::print); 

ВАЖНО: fn.apply(list) – принимает List и создает Stream .

карта

Возьмем Function которая принимает один аргумент и выдает value :

 Function, String> fn = list -> list.toString(); 

И примените эту функцию к каждому элементу (списку) в матрице:

 for (List list : matrix) { String str = fn.apply(list); System.out.print(str); } 

Это именно то, что Stream.map(fn) может выполнить в сжатом виде:

 matrix.stream().map(fn).forEach(System.out::print); 

ВАЖНО: fn.apply(list) – принимает List и создает String .

Операции streamа flatMap и map принимают функцию в качестве входных данных.

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

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

  • Почему Java-streamи отключены?
  • Как преобразовать stream Java 8 в массив?
  • Лучший подход к обработке исключений функциональным способом
  • Скопируйте stream, чтобы избежать «того, что stream уже оперирован или закрыт»
  • Почему Stream не имеет метода toList ()?
  • Список групп Java 8 lambdas на карте
  • Почему findFirst () выбрасывает исключение NullPointerException, если первый найденный элемент равен null?
  • Самый эффективный способ получить последний элемент streamа
  • Почему java.util.Collection не реализует новый интерфейс Stream?
  • Почему комбайнер необходим для метода уменьшения, который преобразует тип в java 8
  • Как создать stream из массива?
  • Давайте будем гением компьютера.