map () e flatMap ()
map()
Apenas pega uma Function como um parâmetro lambda onde T é o elemento e R o elemento de retorno criado usando T. No final, teremos um Stream 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 Type Integer
, usa cada elemento para criar um novo elemento do tipo String
com valor "prefix_"+integer_value
e o imprime.
flatMap()
É útil saber que flatMap () assume uma função F<T, R>
em que
T é um tipo do qual um Stream pode ser construído a partir de / com . Pode ser uma lista (T.stream ()), uma matriz (Arrays.stream (someArray)), etc. qualquer coisa a partir da qual um fluxo possa ser com / ou formulário. no exemplo abaixo, cada desenvolvedor tem muitos idiomas, portanto, desenvolvedor. Idiomas é uma lista e usará um parâmetro lambda.
R é o fluxo resultante que será construído usando T. Sabendo que temos muitas instâncias de T, naturalmente teremos muitos fluxos de R. Todos esses fluxos do tipo R agora serão combinados em um único fluxo 'plano' do tipo R .
Exemplo
Os exemplos de Bachiri Taoufiq vêem sua resposta aqui são simples e fáceis de entender. Apenas para maior clareza, digamos que temos uma equipe de desenvolvedores:
dev_team = {dev_1,dev_2,dev_3}
, com cada desenvolvedor conhecendo vários idiomas:
dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}
Aplicando Stream.map () no dev_team para obter os idiomas de cada dev:
dev_team.map(dev -> dev.getLanguages())
lhe dará essa estrutura:
{
{lang_a,lang_b,lang_c},
{lang_d},
{lang_e,lang_f}
}
que é basicamente a List<List<Languages>> /Object[Languages[]]
. Não é tão bonito, nem Java8!
com Stream.flatMap()
você pode 'achatar' as coisas, uma vez que pega a estrutura acima
e a transforma {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}
, que pode ser usada basicamente como List<Languages>/Language[]/etc
...
portanto, no final, seu 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 map () e flatMap () :
Use map()
quando cada elemento do tipo T do seu fluxo deve ser mapeado / transformado em um único elemento do tipo R. O resultado é um mapeamento do tipo (1 elemento inicial -> 1 elemento final) e o novo fluxo de elementos do tipo R é retornado.
Use flatMap()
quando cada elemento do tipo T do seu fluxo deve ser mapeado / transformado em Coleções de elementos do tipo R. O resultado é um mapeamento do tipo (1 elemento inicial -> n elementos finais) . Essas coleções são mescladas (ou niveladas ) com um novo fluxo de elementos do tipo R. Isso é útil, por exemplo, para representar loops aninhados .
Antes do Java 8:
List<Foo> myFoos = new ArrayList<Foo>();
for(Foo foo: myFoos){
for(Bar bar: foo.getMyBars()){
System.out.println(bar.getMyName());
}
}
Pós Java 8
myFoos
.stream()
.flatMap(foo -> foo.getMyBars().stream())
.forEach(bar -> System.out.println(bar.getMyName()));
map :: Stream T -> (T -> R) -> Stream R
,flatMap :: Stream T -> (T -> Stream R) -> Stream R
.