W Javie 8, co jest różnica między Stream.map()
i Stream.flatMap()
metody?
java
java-8
java-stream
kasiomolina
źródło
źródło
map :: Stream T -> (T -> R) -> Stream R
,flatMap :: Stream T -> (T -> Stream R) -> Stream R
.<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
.map
że lambda odwzorowująca zwracaR
, aflatMap
lambda odwzorowująca zwraca aStream
zR
(Stream<R>
). Strumienie zwrócone przez programflatMap
odwzorowujący są skutecznie łączone. W przeciwnym razie zarównomap
iflatMap
powrótStream<R>
; różnica jest co zwróci lambdas MapperR
vs.Stream<R>
.Odpowiedzi:
Oba
map
iflatMap
mogą być zastosowane do aStream<T>
i oba zwracają aStream<R>
. Różnica polega na tym, żemap
operacja generuje jedną wartość wyjściową dla każdej wartości wejściowej, podczas gdyflatMap
operacja generuje dowolną liczbę (zero lub więcej) wartości dla każdej wartości wejściowej.Odzwierciedlają to argumenty każdej operacji.
map
Operacja trwaFunction
, która jest wywoływana dla każdej wartości w strumieniu wejściowym i wytwarza jedną wartość wynikową, który jest wysyłany do strumienia wyjściowego.flatMap
Operacja trwa funkcję, która koncepcyjnie chce spożywać jedną wartość i produkować dowolną liczbę wartości. Jednak w Javie niewygodne jest, aby metoda zwracała dowolną liczbę wartości, ponieważ metody mogą zwracać tylko zero lub jedną wartość. Można sobie wyobrazić interfejs API, w którym funkcja mapująca dlaflatMap
przyjmuje wartość i zwraca tablicę lub aList
wartości, które są następnie wysyłane do wyjścia. Biorąc pod uwagę, że jest to biblioteka strumieni, szczególnie trafnym sposobem reprezentowania dowolnej liczby zwracanych wartości jest, aby sama funkcja mapująca zwróciła strumień! Wartości ze strumienia zwrócone przez program odwzorowujący są usuwane ze strumienia i przekazywane do strumienia wyjściowego. „Gromady” wartości zwracane przez każde wywołanie funkcji odwzorowującej wcale nie są rozróżniane w strumieniu wyjściowym, dlatego mówi się, że dane wyjściowe zostały „spłaszczone”.Typowe zastosowanie to funkcja odwzorowująca
flatMap
zwracająca,Stream.empty()
jeśli chce wysłać wartości zerowe, lub coś w rodzaju,Stream.of(a, b, c)
jeśli chce zwrócić kilka wartości. Ale oczywiście każdy strumień może zostać zwrócony.źródło
flatMap
operacja jest dokładnym przeciwieństwem mieszkania. Po raz kolejny pozostawmy informatykom, aby zawrócili termin na głowie. Podobnie jak funkcja „przezroczysta”, co oznacza, że nie widzisz nic, co robi, tylko wyniki, a potocznie mówiąc, że chcesz, aby proces był przezroczysty, oznacza, że chcesz widzieć każdą jego część.Stream.flatMap
, jak można się domyślić na podstawie nazwy, jest kombinacją operacjimap
iflat
operacji. Oznacza to, że najpierw zastosujesz funkcję do swoich elementów, a następnie spłaszczysz ją.Stream.map
stosuje tylko funkcję do strumienia bez spłaszczania strumienia.Aby zrozumieć, na czym polega spłaszczenie strumienia, rozważ taką strukturę,
[ [1,2,3],[4,5,6],[7,8,9] ]
która ma „dwa poziomy”. Spłaszczenie oznacza to przekształcenie go w „jeden poziom” strukturze:[ 1,2,3,4,5,6,7,8,9 ]
.źródło
Chciałbym podać 2 przykłady, aby uzyskać bardziej praktyczny punkt widzenia:
Pierwszy przykład użycia mapy:
W pierwszym przykładzie nie ma nic specjalnego, a
Function
zwracaString
wielką literą.Drugi przykład wykorzystujący
flatMap
:W drugim przykładzie przekazywany jest strumień listy. NIE jest to strumień liczb całkowitych!
Jeśli trzeba użyć funkcji transformacji (poprzez mapę), najpierw Strumień należy spłaszczyć na coś innego (Strumień liczb całkowitych).
Usunięcie flatMap powoduje zwrócenie następującego błędu: Operator + jest niezdefiniowany dla typów argumentów List, int.
NIE jest możliwe zastosowanie + 1 do Listy liczb całkowitych!
źródło
Stream<Integer>
zamiast StrumieniaInteger
.Przejrzyj całkowicie post, aby uzyskać jasny pomysł,
mapa vs płaska mapa:
Aby zwrócić długość każdego słowa z listy, zrobilibyśmy coś takiego jak poniżej.
Krótka wersja podana poniżej
Kiedy zbieramy dwie listy, podane poniżej
Bez płaskiej mapy => [1,2], [1,1] => [[1,2], [1,1]] Tutaj dwie listy są umieszczone na liście, więc wynikiem będzie lista zawierająca listy
Z płaską mapą => [1,2], [1,1] => [1,2,1,1] Tutaj dwie listy są spłaszczone i tylko wartości są umieszczane na liście, więc wynik będzie listą zawierającą tylko elementy
Zasadniczo łączy wszystkie obiekty w jeden
## Szczegółowa wersja została podana poniżej: -
Na przykład: -
Zastanów się nad listą [„STOSOWANIE”, „OOOVVVER”], a my próbujemy zwrócić listę taką jak [„STACKOVER”] (zwracając tylko unikalne litery z tej listy) Początkowo robimy coś takiego jak poniżej, aby zwrócić lista [„STACKOVER”] z [„STACK”, „OOOVVVER”]
Problem polega na tym, że Lambda przekazana do metody map zwraca tablicę ciągów dla każdego słowa, więc strumień zwrócony przez metodę mapy jest w rzeczywistości typu Stream, ale potrzebujemy strumienia do reprezentowania strumienia znaków, poniżej obraz ilustruje problem.
Rycina A:
Możesz pomyśleć, że możemy rozwiązać ten problem za pomocą płaskiej mapy,
OK, pozwól nam zobaczyć, jak rozwiązać ten problem za pomocą map i Arrays.stream Przede wszystkim potrzebujesz strumienia znaków zamiast strumienia tablic. Istnieje metoda o nazwie Arrays.stream (), która pobierałaby tablicę i tworzy strumień, na przykład:
Powyższe nadal nie działa, ponieważ teraz otrzymujemy listę strumieni (a dokładniej Stream>). Zamiast tego musimy najpierw przekonwertować każde słowo na tablicę pojedynczych liter, a następnie przekształcić każdą tablicę w osobny strumień
Korzystając z flatMap, powinniśmy być w stanie rozwiązać ten problem, jak poniżej:
flatMap wykonałby mapowanie każdej tablicy nie ze strumieniem, ale z zawartością tego strumienia. Wszystkie poszczególne strumienie, które zostałyby wygenerowane podczas korzystania z mapy (Arrays :: stream) zostaną scalone w jeden strumień. Rycina B ilustruje efekt zastosowania metody flatMap. Porównaj to z mapą na rysunku A. Rysunek B
Metoda flatMap pozwala zastąpić każdą wartość strumienia innym strumieniem, a następnie łączy wszystkie wygenerowane strumienie w jeden strumień.
źródło
Odpowiedź w jednym wierszu:
flatMap
pomaga spłaszczyćCollection<Collection<T>>
aCollection<T>
. W ten sam sposób spłaszczy się równieżOptional<Optional<T>>
wOptional<T>
.Jak widać,
map()
tylko:Stream<List<Item>>
List<List<Item>>
oraz z
flatMap()
:Stream<Item>
List<Item>
To wynik testu z kodu zastosowanego poniżej:
Zastosowany kod :
źródło
Przekazywana funkcja
stream.map
musi zwrócić jeden obiekt. Oznacza to, że każdy obiekt w strumieniu wejściowym daje dokładnie jeden obiekt w strumieniu wyjściowym.Przekazywana funkcja
stream.flatMap
zwraca strumień dla każdego obiektu. Oznacza to, że funkcja może zwrócić dowolną liczbę obiektów dla każdego obiektu wejściowego (w tym żaden). Powstałe strumienie są następnie łączone w jeden strumień wyjściowy.źródło
Department
s w swojej organizacji. Każdy dział ma od 0 do nsEmployee
. Potrzebny jest strumień wszystkich pracowników. Więc co robisz? Piszesz metodę flatMap, która przyjmuje dział i zwraca strumień jego pracowników.flatMap
? Podejrzewam, że może to mieć charakter przypadkowy i nie ilustruje kluczowego przypadku użycia ani powodu, dla któregoflatMap
istnieje. (Ciąg dalszy poniżej ...)flatMap
jest dostosowanie się do błędów, które pojawiałyby się podczas korzystaniamap
. Jak radzić sobie z przypadkami, w których jeden lub więcej elementów w oryginalnym zestawie nie można zmapować na element wyjściowy? Wprowadzając zestaw pośredni (powiedzmyOptional
lubStream
) dla każdego obiektu wejściowego,flatMap
pozwala wykluczyć „nieprawidłowe” obiekty wejściowe (lub tak zwane „złe jabłka” w duchu stackoverflow.com/a/52248643/107158 ) z zestaw końcowy.dla mapy mamy listę elementów i (funkcja, akcja) f, więc:
a dla płaskiej mapy mamy listę elementów i mamy (funkcję, akcję) fi chcemy, aby wynik został spłaszczony:
źródło
Mam wrażenie, że większość odpowiedzi tutaj komplikuje prosty problem. Jeśli już rozumiesz, jak
map
działa, to powinno być dość łatwe do zrozumienia.Są przypadki, w których możemy skończyć z niechcianymi strukturami zagnieżdżonymi podczas używania
map()
,flatMap()
metoda została zaprojektowana w celu przezwyciężenia tego przez unikanie zawijania.Przykłady:
1
Możemy uniknąć zagnieżdżania list, używając
flatMap
:2)
gdzie:
źródło
List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());
. Powinno byćStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
Artykuł Oracle na temat Opcjonalnego podkreśla różnicę między mapą a płaską mapą:
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
źródło
Nie jestem pewien, czy mam na to odpowiedzieć, ale za każdym razem, gdy spotykam się z kimś, kto tego nie rozumie, używam tego samego przykładu.
Wyobraź sobie, że masz jabłko. A
map
przekształca to jabłkoapple-juice
na przykład lub mapowanie jeden na jeden .Weź to samo jabłko i wyciągnij z niego tylko nasiona, czyli to, co
flatMap
robi, albo jedno do wielu , jedno jabłko jako wkład, wiele nasion jako wynik.źródło
flatMap
przypadku, czy najpierw zbierasz nasiona z każdego jabłka w osobnych torebkach, po jednej torebce na jabłko, zanim wlejesz wszystkie torebki do jednej torebki?flatmap
nie był zbyt leniwy, ale od java-10 jest leniwyflatMap + lazy
, założę się, że będzie kilka odpowiedzi.map () i flatMap ()
map()
Po prostu przyjmuje funkcję param lambda, w której T jest elementem, a R element powrotu zbudowany za pomocą T. Na końcu będziemy mieli strumień z obiektami typu R. Prostym przykładem może być:
Po prostu bierze elementy od 1 do 5 typu
Integer
, używa każdego elementu do zbudowania nowego elementu z typuString
o wartości"prefix_"+integer_value
i drukuje go.flatMap()
Warto wiedzieć, że flatMap () przyjmuje funkcję
F<T, R>
gdzieT jest rodzajem, z którego można budować strumień z / z . Może to być lista (T.stream ()), tablica (Arrays.stream (someArray)) itp. Cokolwiek, z czego strumień może być w / lub w formie. w poniższym przykładzie każdy deweloper ma wiele języków, więc dev. Języki to lista i będzie używać parametru lambda.
R jest powstałym Strumieniem, który zostanie zbudowany przy użyciu T. Wiedząc, że mamy wiele wystąpień T, naturalnie będziemy mieli wiele Strumieni od R. Wszystkie te Strumienie Typu R zostaną teraz połączone w jeden pojedynczy „płaski” Strumień od Typu R .
Przykład
Przykłady Bachiri Taoufiq, które widzą tutaj swoją odpowiedź, są proste i łatwe do zrozumienia. Dla jasności, powiedzmy, że mamy zespół programistów:
, przy czym każdy programista zna wiele języków:
Zastosowanie Stream.map () na dev_team, aby uzyskać języki każdego dewelopera:
da ci tę strukturę:
co jest w zasadzie a
List<List<Languages>> /Object[Languages[]]
. Nie tak bardzo ładnie, ani Java8 !!dzięki
Stream.flatMap()
czemu możesz „spłaszczyć” rzeczy, ponieważ bierze powyższą strukturęi przekształca ją
{lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}
, co można zasadniczo wykorzystać jakoList<Languages>/Language[]/etc
...więc w końcu twój kod miałby więcej sensu:
lub po prostu:
Kiedy używać map () i flatMap () :
Użyj,
map()
gdy każdy element typu T ze strumienia ma być odwzorowany / przekształcony w pojedynczy element typu R. Wynikiem jest mapowanie typu (1 element początkowy -> 1 element końcowy) i nowy strumień elementów typu R jest zwracany.Użyj,
flatMap()
gdy każdy element typu T ze strumienia ma zostać zmapowany / przekształcony w kolekcję elementów typu R. Wynikiem jest mapowanie typu (1 element początkowy -> n elementów końcowych) . Kolekcje te są następnie łączone (lub spłaszczane ) z nowym strumieniem elementów typu R. Jest to przydatne na przykład do reprezentowania zagnieżdżonych pętli .Przed Java 8:
Post Java 8
źródło
Mapa: - Ta metoda przyjmuje jedną funkcję jako argument i zwraca nowy strumień składający się z wyników wygenerowanych przez zastosowanie przekazanej funkcji do wszystkich elementów strumienia.
Wyobraźmy sobie, że mam listę wartości całkowitych (1,2,3,4,5) i jeden interfejs funkcji, którego logika jest kwadratem podanej liczby całkowitej. (e -> e * e).
wynik:-
Jak widać, wyjściem jest nowy strumień, którego wartości są kwadratem wartości strumienia wejściowego.
http://codedestine.com/java-8-stream-map-method/
FlatMap: - Ta metoda przyjmuje jedną funkcję jako argument, ta funkcja przyjmuje jeden parametr T jako argument wejściowy i zwraca jeden strumień parametru R jako wartość zwracaną. Zastosowanie tej funkcji do każdego elementu tego strumienia powoduje wygenerowanie strumienia nowych wartości. Wszystkie elementy tych nowych strumieni generowanych przez każdy element są następnie kopiowane do nowego strumienia, który będzie zwracaną wartością tej metody.
Wyobraźmy sobie, że mam listę przedmiotów studenckich, gdzie każdy uczeń może wybrać wiele przedmiotów.
wynik:-
Jak widać, wyjściem jest nowy strumień, którego wartości są zbiorem wszystkich elementów strumieni zwracanych przez każdy element strumienia wejściowego.
[S1, S2, S3] -> [{„historia”, „matematyka”, „geografia”}, {„ekonomia”, „biologia”}, {„nauka”, „matematyka”}] -> podejmuj unikalne tematy - > [ekonomia, biologia, geografia, nauka, historia, matematyka]
http://codedestine.com/java-8-stream-flatmap-method/
źródło
.map służy do mapowania A -> B.
konwertuje dowolny element
A
na dowolny elementB
. Javadoc.flatMap jest A -> Stream <B> concatinating
to --1 przekształca dowolny element
A
wStream< B>
, a następnie --2 łączy wszystkie strumienie w jeden (płaski) strumień. JavadocUwaga 1: Chociaż ten ostatni przykład jest dopasowany do strumienia prymitywów (IntStream) zamiast strumienia obiektów (Stream), nadal ilustruje ideę
.flatMap
.Uwaga 2: Pomimo nazwy metoda String.chars () zwraca wartości ints. Tak więc rzeczywista kolekcja będzie:,
[100, 111, 103, 99, 97, 116]
gdzie100
jest kod'd'
,111
jest kodem'o'
itd. Ponownie, dla celów ilustracyjnych, jest prezentowany jako [d, o, g, c, a, t].źródło
Prosta odpowiedź.
map
Operację można produkowaćStream
zStream
ExStream<Stream<Integer>>
flatMap
operacja przyniesie tylkoStream
coś. DAWNYStream<Integer>
źródło
Również dobra analogia może być z C #, jeśli znasz. Zasadniczo C #
Select
podobny do javamap
i C #SelectMany
javaflatMap
. To samo dotyczy kolekcji Kotlin.źródło
Jest to bardzo mylące dla początkujących. Podstawowa różnica polega na tym, że
map
emituje jeden element dla każdego wpisu na liście iflatMap
jest w zasadzie operacjąmap
+flatten
. Aby być bardziej zrozumiałym, użyj flatMap, gdy potrzebujesz więcej niż jednej wartości, np. Gdy oczekujesz, że pętla zwróci tablice, flatMap będzie naprawdę pomocny w tym przypadku.Napisałem o tym bloga, możesz to sprawdzić tutaj .
źródło
Przesyłaj strumieniowo operacje
flatMap
imap
akceptuj funkcję jako dane wejściowe.flatMap
oczekuje, że funkcja zwróci nowy strumień dla każdego elementu strumienia i zwróci strumień, który łączy wszystkie elementy strumieni zwróconych przez funkcję dla każdego elementu. Innymi słowy,flatMap
dla każdego elementu ze źródła wiele funkcji zostanie utworzonych przez funkcję. http://www.zoftino.com/java-stream-examples#flatmap-operationmap
oczekuje, że funkcja zwróci przekształconą wartość i zwróci nowy strumień zawierający przekształcone elementy. Innymi słowy,map
dla każdego elementu ze źródła jeden przekształcony element zostanie utworzony przez funkcję. http://www.zoftino.com/java-stream-examples#map-operationźródło
flatMap()
wykorzystuje również częściową leniwą ocenę strumieni. Odczyta pierwszy strumień i tylko w razie potrzeby przejdzie do następnego strumienia. Zachowanie jest szczegółowo wyjaśnione tutaj: czy flatMap gwarantuje lenistwo?źródło
Jeśli myślisz
map()
jako iteracja (for
pętla jednopoziomowa ),flatmap()
to iteracja dwupoziomowa (jakfor
pętla zagnieżdżona ). (Wprowadź każdy iterowany elementfoo
i powtórzfoo.getBarList()
w nim iteracjębarList
)map()
: weź strumień, zrób coś z każdym elementem, zbierz pojedynczy wynik każdego procesu, wyślij kolejny strumień. Definicja „zrób coś funkcji” jest niejawna. Jeśli powstaje przetwarzanie dowolnego elementunull
,null
służy do komponowania końcowego strumienia. Tak więc liczba elementów w strumieniu wynikowym będzie równa liczbie strumienia wejściowego.flatmap()
: weź strumień elementów / strumieni i funkcję (wyraźna definicja), zastosuj funkcję do każdego elementu każdego strumienia i zbierz cały wynikowy strumień pośredni, aby był większym strumieniem („spłaszczanie”). Jeśli wynikiem jest przetwarzanie dowolnego elementunull
, pusty strumień jest dostarczany do ostatniego etapu „spłaszczania”. Liczba elementów w wynikowym strumieniu jest sumą wszystkich uczestniczących elementów we wszystkich danych wejściowych, jeśli dane wejściowe to kilka strumieni.źródło