Qual é a diferença entre os métodos map() e flatMap() em Java 8?

em Java 8, Qual é a diferença entre Stream.map() e Stream.flatMap() métodos?

Author: cassiomolin, 2014-10-31

19 answers

Ambos map e flatMap podem ser aplicados a Stream<T> e ambos devolvem a Stream<R>. A diferença é que a operação map produz um valor de saída para cada valor de entrada, enquanto a operação flatMap produz um número arbitrário (zero ou mais) para cada valor de entrada.

Isto reflecte-se nos argumentos de cada operação.

A operação map toma um Function, que é chamado para cada valor no fluxo de entrada e produz um valor de resultado, que é enviado para o fluxo de saída.

A operação flatMap tem uma função que conceptualmente quer consumir um valor e produzir um número arbitrário de valores. No entanto, em Java, é complicado para um método retornar um número arbitrário de Valores, uma vez que os métodos podem retornar apenas zero ou um valor. Pode-se imaginar uma API onde a função mapper para flatMap toma um valor e devolve um array ou um List de valores, que são então enviados para a saída. Dado que esta é a biblioteca streams, um particularmente apt maneira de representar um número arbitrário de valores de retorno é para a função mapper em si para retornar um fluxo! Os valores do fluxo retornado pelo mapper são drenados do fluxo e são passados ao fluxo de saída. Os " clumps "de valores retornados por cada chamada para a função mapper não são distinguidos em tudo na corrente de saída, assim a saída é dita ter sido" achatada."

O uso típico é para a função mapper de flatMap para retornar Stream.empty() Se quiser para enviar valores zero, ou algo como Stream.of(a, b, c) Se quiser devolver vários valores. Mas é claro que qualquer Riacho pode ser devolvido.

 905
Author: Stuart Marks, 2016-06-18 17:13:16

Stream.flatMap, como pode ser adivinhado pelo seu nome, é a combinação de uma operação map e uma operação flat. Isso significa que você primeiro aplica uma função aos seus elementos, e então achatá-la. {[3] } apenas aplica uma função à corrente sem nivelar a corrente.

Para compreender em que consiste um fluxo, considere uma estrutura como {[4] } que tem "dois níveis". Achatar isto significa transformá-lo numa estrutura de "um nível": [ 1,2,3,4,5,6,7,8,9 ].

 589
Author: Dici, 2016-05-17 21:43:37

Eu gostaria de dar 2 Exemplos para obter um mais ponto de vista prático:
primeiro exemplo de utilização de map:

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

Nada de especial no primeiro exemplo, um Function é aplicado para devolver o {[4] } em maiúsculas.

Segundo exemplo de utilização de 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);
}

No segundo exemplo, um fluxo de lista é passado. Não é um fluxo de inteiros!
se uma função de transformação tem que ser usada (através do mapa), então primeiro o Stream tem que ser achatado para algo mais (um fluxo de inteiro).
se flatMap for removido então o seguinte erro é devolvido: o operador + está indefinido para a lista de tipos de argumentos, int.
não é possível aplicar + 1 num List de inteiros!

 263
Author: Rudy Vissers, 2020-06-24 08:38:07

por favor, atravesse o post completamente para ter uma ideia clara,

Mapa vs mapa plano:

Para devolver um comprimento de cada palavra de uma lista, nós faríamos algo como abaixo..

Versão resumida abaixo

Quando coletamos duas listas, dadas abaixo

Sem mapa plano => [1,2],[1,1] => [[1,2],[1,1]] Aqui duas listas são colocados dentro de uma lista, então a saída será da lista que contém listas

Com mapa plano => [1,2],[1,1] => [1,2,1,1] Aqui duas listas são achatadas e apenas os valores são colocados na lista, então, a saída será de lista contendo apenas os elementos

Basicamente, junta todos os objectos a um.

# # a versão detalhada foi dada abaixo: -

Por exemplo:-
Considere uma lista ["PILHA", "OOOVVVER"] e estamos tentando retornar uma lista como ["STACKOVER"] (devolvendo apenas letras únicas daquela lista) Inicialmente, faríamos algo como abaixo para devolver uma lista ["STACKOVER"] de ["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());
  }
}

Aqui o problema é que o Lambda passado para o método do mapa devolve um array de texto para cada palavra, por isso o fluxo devolvido pelo método do mapa é na verdade de fluxo de tipo, mas o que precisamos é de fluxo para representar um fluxo de caracteres, abaixo a imagem ilustra o problema.

Figura A:

enter image description here

Você pode pensar que, nós podemos resolver este problema usando flatmap,
OK, vamos ver como resolver isso usando Map e Arrays.stream Primeiro, vais precisar de um fluxo de personagens em vez de um fluxo de arrays. Há um método chamado Arrays.stream () que pegaria um array e produziria um stream, por exemplo:

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

O acima ainda não funciona, porque agora acabamos com uma lista de córregos (mais precisamente, Stream>), em vez disso, devemos primeiro converter cada palavra em um conjunto de letras individuais e, em seguida, fazer cada array em um fluxo separado

Usando flatMap devemos ser capazes de corrigir este problema como abaixo:

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 executaria mapeamento de cada matriz não com fluxo, mas com o conteúdo desse fluxo. Todos os fluxos individuais que seriam gerados ao usar o mapa (Arrays:: stream) se fundem em um único fluxo. A figura B ilustra o efeito da utilização do método flatMap. Compare-o com o que o mapa faz na Figura A. Figura B enter image description here

O método flatMap permite substituir cada valor de um fluxo por outro fluxo e, em seguida, junta todos os fluxos gerados em um único fluxo.

 182
Author: TechDog, 2019-05-13 02:51:15

Uma Resposta de linha: flatMap ajuda a achatar um Collection<Collection<T>> em um Collection<T>. Da mesma forma, também achatará um Optional<Optional<T>> em Optional<T>.

enter image description here

Como podes ver, com map() apenas:

  • o tipo intermédio é Stream<List<Item>>
  • o tipo de retorno é List<List<Item>>

E com flatMap():

  • o tipo intermédio é Stream<Item>
  • o tipo de retorno é List<Item>

Este é o resultado do teste do Código utilizado abaixo:

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

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

Código utilizado:

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);
  }
}
 127
Author: Hoa Nguyen, 2019-04-02 14:47:11

A função que passa para stream.map Tem de devolver um objecto. Isso significa que cada objeto no fluxo de entrada resulta em exatamente um objeto no fluxo de saída.

A função que passa para stream.flatMap devolve um fluxo para cada objecto. Isso significa que a função pode retornar qualquer número de objetos para cada objeto de entrada (incluindo nenhum). Os fluxos resultantes são então concatenados a um fluxo de saída.

 46
Author: Philipp, 2014-10-31 22:58:56

Para um mapa temos uma lista de elementos e uma (função, acção) F so:

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

E para o mapa plano temos uma lista de elementos e temos uma (função,acção) f e queremos que o resultado seja achatado :

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
 32
Author: Bachiri Taoufiq Abderrahman, 2017-10-08 03:19:10
Tenho a sensação de que a maioria das respostas aqui complica demasiado o problema simples. Se você já entende como o map funciona que deve ser bastante fácil de entender.

Há casos em que podemos acabar com estruturas aninhadas indesejadas quando usamos map(), o método flatMap() foi concebido para ultrapassar isto evitando envolver.


Exemplos:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());
Podemos evitar ter listas aninhadas usando 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));

Em que:

private Optional<String> findById(Integer id)
 28
Author: Grzegorz Piwowarek, 2018-04-15 20:08:16

.mapa é para A -> B mapeamento

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

converte qualquer item A em qualquer item B. Javadoc


.flatMap é para A -> Stream< B> concatinating

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

Ele --1 converte qualquer item A em Stream< B>, Então -- 2 concatenaliza todos os fluxos em um único fluxo (plano). Javadoc


Nota 1: Embora este último exemplo flutue num fluxo de primitivos (IntStream) em vez de um fluxo de objetos (Stream), ele ainda ilustra a idéia do .flatMap.

Nota 2: Apesar do nome, String.o método chars () devolve ints. Então, a coleção real será: [100, 111, 103, 99, 97, 116] , em que {[10] } é o código de 'd', 111 é o código de 'o' etc. Mais uma vez, para fins ilustrativos, é apresentado como [d, o, g, c, A, t].

 26
Author: epox, 2020-06-24 11:38:37
O artigo da Oracle sobre opções destaca esta diferença entre mapa e mapa plano:
String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");
Infelizmente, este código não compila. Por quê? O computador variável é do tipo Optional<Computer>, por isso é perfeitamente correcto chamar o método do mapa. Contudo, o getSoundcard () devolve um objecto do tipo Opcional. Isto significa que o resultado da operação map é um objecto do tipo Optional<Optional<Soundcard>>. Como resultado, o convite para o getUSB () é inválido porque o opcional mais externo contém como seu value outro opcional, que, claro, não suporta o getUSB () metodo.

Com as transmissões, O método flatMap assume uma função como argumento, que devolve outro Riacho. Esta função é aplicada a cada elemento de um riacho, o que resultaria num riacho de riachos. Entanto, flatMap tem o efeito de substituir cada fluxo gerado pelo o conteúdo daquele Riacho. Em outras palavras, todos os fluxos separados que são gerados pela função ser amalgamado ou "achatado" em um um único Riacho. O que queremos aqui é algo semelhante, mas queremos "nivelar" uma opção de dois níveis em um.

Opcional também suporta um método flatMap. O seu objectivo é aplicar a função de transformação no valor de um opcional (tal como o mapa operação faz) e então nivelar o resultado de dois níveis opcional em um único.

Então, para fazer nosso código correto, precisamos reescrevê-lo como segue usando mapa plano:
String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

O primeiro mapa plano garante que um Optional<Soundcard> é devolvido em vez de um Optional<Optional<Soundcard>>, e o segundo plano plano atinge o mesmo propósito de devolver um Optional<USB>. Note - se que o a terceira chamada só precisa de ser um mapa () porque o getVersion () devolve um String em vez de um objeto opcional.

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

 23
Author: Rusty Core, 2017-02-24 20:22:21

Mapa () e plano()

  1. map()

Basta tomar uma função um lambda param onde T é elemento e r o elemento de retorno construído usando T. No final teremos um fluxo com objetos do tipo R. um exemplo simples pode ser:

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

Ele simplesmente pega os elementos 1 a 5 do tipo Integer, usa cada elemento para construir um novo elemento do tipo String com o valor "prefix_"+integer_value e imprime-o.

  1. flatMap()
É útil saber esse mapa plano() toma uma função F<T, R> onde
  • T é um tipo de que um riacho pode ser construído a partir de / com . Pode ser uma lista (T. stream ()), uma array (Arrays.stream (somarray), etc.. qualquer coisa a partir da qual um riacho pode estar com/ou forma. no exemplo abaixo cada dev tem muitas línguas, assim dev. Languages é uma lista e vai usar um parâmetro lambda.

  • R é o fluxo resultante que será construído usando T. sabendo que temos muitos exemplos de T, teremos naturalmente muitos córregos de R. todos esses córregos do tipo R serão agora combinados em um único fluxo 'plano' do tipo R.

Exemplo

Os exemplos de Bachiri Taoufiq veja a sua resposta aqui são simples e fáceis de entender. Só para esclarecer, digamos que temos uma equipa de desenvolvedores:
dev_team = {dev_1,dev_2,dev_3}

, com cada desenvolvedor a conhecer muitas línguas:

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

Aplicação Transmissão.mapa () on dev_team to get the languages of each dev:

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

Dar-lhe-á esta estrutura:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}
O que é basicamente um List<List<Languages>> /Object[Languages[]]. Nem tão bonito, nem tão parecido com o Java8!!

Com Stream.flatMap()pode-se 'achatar' as coisas à medida que se retira a estrutura acima
e transforma-o em {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, que pode basicamente ser usado como List<Languages>/Language[]/etc...

Então, no final, o teu código faria mais sentido assim:
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();

Ou simplesmente:

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

Quando usar o map () e usar mapa plano():

  • Use map() quando cada elemento do tipo T, a partir de seu fluxo é suposto ser mapeada/transformada única elemento do tipo R. O resultado é um mapeamento do tipo (1 elemento de início -> 1 elemento) e o novo fluxo de elementos do tipo R é retornado.

  • Use flatMap() quando cada elemento do tipo T do seu fluxo é suposto mapear/transformar para uma Colecções de elementos do tipo R. O resultado é um mapeamento do tipo (1 elemento inicial -> n elementos finais) . Essas Coleções são, em seguida, mesclado (ou achatada) para um novo fluxo de elementos do tipo R. Isso é útil, por exemplo, para representar loops aninhados.

Pre Java 8:

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

Depois De Java 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));
 22
Author: arthur, 2020-02-26 11:59:35
Não tenho a certeza se devo responder a isto, mas sempre que enfrento alguém que não entende isto, uso o mesmo exemplo. Imagina que tens uma maçã. A map está a transformar essa maçã em apple-juice por exemplo, ou um mapeamento de um para um .

Pega nessa mesma maçã e tira apenas as sementes dela, é isso que flatMap faz, ou um um a muitos , uma maçã como entrada, muitas sementes como saída.

 18
Author: Eugene, 2018-09-09 21:03:54

Mapa:- Este método leva uma função como um argumento e retorna um novo fluxo consistindo dos resultados gerados pela aplicação da função passada a todos os elementos do fluxo.

Vamos imaginar, eu tenho uma lista de valores inteiros ( 1,2,3,4,5 ) e uma interface de função cuja lógica é quadrada do inteiro passado. (e - > E * e ).

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

Resultado: -

[1, 4, 9, 16, 25]

Como pode ver, uma saída é uma nova corrente cujos valores são quadrados dos valores da entrada fluxo.

[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/

Plano :- Este método leva uma função como um argumento, esta função aceita um parâmetro T como um argumento de entrada e retorna um fluxo do parâmetro R como um valor de retorno. Quando esta função é aplicada a cada elemento desta corrente, ela produz uma corrente de novos valores. Todos os elementos destes novos fluxos gerados por cada elemento são então copiados para um novo fluxo, que será um valor de retorno deste método.

Vamos ver, eu tenho uma lista de objetos estudantis, onde cada estudante pode optar por várias disciplinas.
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);

Resultado: -

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

Como você pode ver, uma saída é uma nova corrente cujos valores são uma coleção de todos os elementos dos fluxos retornam por cada elemento da Corrente de entrada.

[ S1 , S2, S3 ] -> [{"history","math","geography"}, {"economics","biology"}, {"science","math"} ] -> take unique subjects - > [economics, biology, geography, Ciência, História, Matemática

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

 13
Author: lalitbhagtani, 2018-07-27 14:51:17

Se você pensa map() como uma iteração (um nível for loop), flatmap() é uma iteração de dois níveis(como um loop nested for). (Digite cada elemento iterado foo, e faça foo.getBarList() e itere nisso barList de novo)


map(): pega num fluxo, faz algo a cada elemento, recolhe o único resultado de cada processo, emite outro fluxo. A definição de "fazer algo função" está implícita. Se o processo de qualquer elemento resultar em null, null é utilizado para compor a fluxo. Assim, o número de elementos no fluxo resultante será igual ao número de fluxo de entrada.

flatmap(): Pegue um fluxo de elementos / fluxos e uma função (definição explícita), aplique a função a cada elemento de cada fluxo, e colete todo o fluxo intermediário resultante para ser um fluxo maior ("achatamento"). Se o processo de qualquer elemento resultar em null, o fluxo vazio é fornecido ao passo final de "achatamento". O número de elementos no resultado stream, é o total de todos os elementos participantes em todas as entradas, se a entrada for vários fluxos.

 3
Author: WesternGun, 2019-11-29 13:07:32
Resposta simples.

A operação map pode produzir um Stream de Stream.EX Stream<Stream<Integer>>

flatMap a operação só produzirá algo. EX Stream<Integer>

 2
Author: Melad Basilius, 2019-03-15 14:44:09

Também uma boa analogia pode ser com C# Se estiver familiarizado. Basicamente C# Select semelhante ao java map e C# SelectMany java flatMap. O mesmo se aplica ao Kotlin para colecções.

 1
Author: Arsenius, 2017-06-14 04:12:30
Isto é muito confuso para principiantes. A diferença básica é que map emite um item para cada item da lista e flatMap é basicamente um map + flatten operação. Para ser mais claro, use flatMap quando necessitar de mais de um valor, por exemplo, quando estiver à espera de um loop para devolver as arrays, flatMap será realmente útil neste caso.

Eu escrevi um blog sobre isso, você pode conferir aqui.

 1
Author: Niraj Chauhan, 2017-10-27 07:43:23

As operações de Fluxo flatMap e map aceitam uma função como entrada.

flatMap espera que a função retorne um novo fluxo para cada elemento do fluxo e retorna um fluxo que combina todos os elementos dos fluxos retornados pela função para cada elemento. Em outras palavras, com flatMap, para cada elemento da fonte, vários elementos serão criados pela função. http://www.zoftino.com/java-stream-examples#flatmap-operation

map espera que função para devolver um valor transformado e retorna um novo fluxo contendo os elementos transformados. Em outras palavras, com map, para cada elemento da fonte, um elemento transformado será criado pela função. http://www.zoftino.com/java-stream-examples#map-operation

 1
Author: Arnav Rao, 2018-07-27 13:09:28

flatMap() também tira vantagem da avaliação parcial preguiçosa de córregos. Ele vai ler o fluxo de punho e só quando necessário, vai para o próximo fluxo. O comportamento é explicado em detalhe aqui: é garantido flatMap ser preguiçoso?

 0
Author: S.K., 2018-09-24 17:36:11