Jaka jest różnica między flatmap i switchmap w RxJava?


149

Rxjava doc definicja switchmap jest dość niejasne i linki do tej samej strony jak flatmap. Jaka jest różnica między tymi dwoma operatorami?


1
O tym prowadzi do tej samej strony, co mapa płaska . To naprawdę prawda. Ale przewiń w dół do sekcji Informacje specyficzne dla języka i otwórz interesujący operator. Myślę, że powinno to być zrobione automatycznie z TOC, ale ... Możesz również zobaczyć ten sam obraz w javadoc .
Ruslan Stelmachenko

Odpowiedzi:


180

Zgodnie z dokumentacją ( http://reactivex.io/documentation/operators/flatmap.html )

switchMapjest jak flatMap, ale będzie emitować tylko przedmioty z nowej zaobserwować aż nowe zdarzenie jest emitowany od obserwowalnych źródłowego.

Diagram w marmurze dobrze to pokazuje. Zwróć uwagę na różnicę w diagramach:

W switchMapdrugiej oryginalnej emisji ( zielony marmur ) nie emituje drugiej zmapowanej emisji ( zielony kwadrat ), ponieważ trzecia pierwotna emisja ( niebieski marmur ) rozpoczęła się i już wyemitowała swoją pierwszą zmapowaną emisję ( niebieski diament ). Innymi słowy, ma miejsce tylko pierwsza z dwóch zmapowanych emisji zielonych; żaden zielony kwadrat nie jest emitowany, ponieważ niebieski diament go pokonał.

W programie flatMapzostaną wyemitowane wszystkie zmapowane wyniki, nawet jeśli są „nieaktualne”. Innymi słowy, zdarzają się zarówno pierwsza , jak i druga zmapowana zielona emisja - wyemitowany zostałby zielony kwadrat (gdyby używali spójnej funkcji mapy; ponieważ tego nie zrobili, zobaczysz drugi zielony diament, mimo że jest emitowany po pierwszy niebieski diament)

switchMap w switchMap, jeśli oryginalne obserwowalne emitują coś nowego, poprzednie emisje nie tworzą już mapowanych obserwabli;  jest to skuteczny sposób na uniknięcie przestarzałych wyników

flatMap

w switchMap, jeśli oryginalne obserwowalne emitują coś nowego, poprzednie emisje nie tworzą już mapowanych obserwabli;  jest to skuteczny sposób na uniknięcie nieaktualnych wyników


4
Dzięki, diagram jest bardzo pomocny. Czy znasz przykład z prawdziwego świata, w którym można by użyć switchMap?
Julian Go

1
@JulianGo jest tutaj przykład: github.com/samuelgruetter/rx-playground/blob/master/… Używa .map(func).switch, ale to to samo co .switchMap(func).
Samuel Gruetter

2
Na wypadek, gdyby ktoś nadal potrzebował rzeczywistego przykładu switchMap, może kliknąć ten link i zrozumie, kiedy użyć swicthMap zamiast flatMap.
hermannovich

2
Na przykład używając SwitchMap od Ben Lesh przy użyciu RxJs5 - zobacz minuty 25-26 tutaj - youtube.com/watch?v=3LKMwkuK0ZE dla mnie, płaska mapa została już zrozumiana ...
arcseldon

7
Marmurowy diagram dobrze to pokazuje? Co? Myślę, że jeśli już rozumiesz mapę przełączników.
Helzgate

166

Natknąłem się na to podczas implementacji „wyszukiwania błyskawicznego” - tj. Gdy użytkownik wpisuje w polu tekstowym, a wyniki pojawiają się niemal w czasie rzeczywistym po każdym naciśnięciu klawisza. Wydaje się, że rozwiązaniem jest:

  1. Miej temat, taki jak PublishSubject of String
  2. W polu tekstowym zmień wywołanie zwrotne, wywołaj .onNext (tekst)
  3. zastosuj filtr .debounce, aby ograniczyć zapytania do serwera
  4. zastosuj .switchMap, aby wykonać zapytanie do serwera - pobierając wyszukiwany termin i zwracając Observable of SearchResponse
  5. zastosuj .subscribe za pomocą metody, która używa SearchResponse i aktualizuje interfejs użytkownika.

W przypadku flatMap wyniki wyszukiwania mogą być nieaktualne, ponieważ odpowiedzi wyszukiwania mogą nie być w kolejności. Aby to naprawić, należy użyć switchMap, ponieważ zapewnia to anulowanie subskrypcji starego obserwowalnego po dostarczeniu nowszego.

Podsumowując, flatMap powinien być używany, gdy wszystkie wyniki mają znaczenie, niezależnie od ich czasu, a switchMap powinien być używany, gdy wyniki pochodzą tylko z ostatniej obserwowalnej materii.


Możesz sprawdzić ten przykład w GitHub
Cabezas

95

Nie flatMap dyskusja jest kompletna bez porównywania i kontrastujący z switchMap, concatMapi concatMapEager.

Wszystkie te metody wymagają Func1przekształcenia strumienia w Observables, które są następnie emitowane; Różnica polega na tym, że zwracane Observables są subskrybowane i anulowane oraz czy i kiedy te emisje tych Observables są emitowane przez danego ____Mapoperatora.

  • flatMapsubskrybuje jak najwięcej wyemitowanych Observables. (Jest to numer zależny od platformy. Np. Niższa liczba na Androidzie) Użyj tego, gdy zamówienie NIE jest ważne i chcesz, aby emisje były jak najszybciej.
  • concatMapsubskrybuje pierwszy Observablei subskrybuje następny Observabledopiero po zakończeniu poprzedniego. Użyj tego, gdy porządek jest ważny i chcesz oszczędzać zasoby. Doskonałym przykładem jest odroczenie połączenia sieciowego przez sprawdzenie pamięci podręcznej w pierwszej kolejności. Zwykle może to nastąpić po znaku .first()lub, .takeFirst()aby uniknąć wykonywania niepotrzebnej pracy.

    http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/

  • concatMapEagerdziała podobnie, ale subskrybuje jak najwięcej (zależnie od platformy), ale będzie emitować dopiero po zakończeniu poprzedniej Observable. Idealne, gdy musisz wykonać dużo równoległego przetwarzania, ale (w przeciwieństwie do flatMap) chcesz zachować pierwotną kolejność.

  • switchMapzasubskrybuje ostatni Observablenapotkany plik i anuluje subskrypcję wszystkich poprzednich Observable. Jest to idealne rozwiązanie w przypadkach takich jak sugestie wyszukiwania: gdy użytkownik zmieni zapytanie wyszukiwania, stare żądanie nie jest już interesujące, więc jest anulowane, a dobrze działający punkt końcowy interfejsu API anuluje żądanie sieciowe.

Jeśli zwracasz Observablepliki, które nie są subscribeOninnym wątkiem, wszystkie powyższe metody mogą zachowywać się podobnie. To ciekawe i użyteczne zachowanie pojawia się, gdy pozwalasz zagnieżdżonym Observables działać na ich własnych wątkach. Następnie można uzyskać uzyskać wiele korzyści z przetwarzania równoległego i inteligentnie wypisywania lub nie subskrybowania z ObservableS, które nie interesują się swoimi Subscribers

  • ambmoże być również interesujące. Biorąc pod uwagę dowolną liczbę Observables, emituje te same elementy, które Observableemituje pierwszy, który cokolwiek wyemitował. Może to być przydatne, gdy masz wiele źródeł, które mogą / powinny zwracać to samo i chcesz wydajności. np. sortowanie, możesz ambposortować szybko przez scalanie i użyć tego, co było szybsze.

1
If you are returning Observables that don't subscribeOn another thread, all of the above methods may behave much the same.- każde wyjaśnienie, z switchMap vs flatMapjakim się spotkałem wcześniej, pomijało ten ważny aspekt, teraz wszystko jest jaśniejsze. Dziękuję Ci.
Andy Res

55

switchMap był kiedyś nazywany flatMapLatest w RxJS 4.

Po prostu przekazuje wydarzenia z ostatniego Observable i anuluje subskrypcję z poprzedniego.


@EpicPandaForce Chociaż jest to niespójne z funkcją CombineLatest, która emituje najnowsze wartości za każdym razem, gdy emituje źródło obserwowalne (nie emituje raz).
Michael Fry,

2
Częściowo powodem, dla którego nazywa się switchMap, jest to, że możesz zaimplementować ten operator samodzielnie, używając o.map (...). Switch (). Chociaż wtedy wyobrażałbym sobie, że byłby to mapSwitch, który nie wydaje się tak łatwo zsuwać się z języka.
Niall Connaughton

7

Map, FlatMap, ConcatMap i SwitchMap stosują funkcję lub modyfikują dane emitowane przez Observable.

  • Mapa modyfikuje każdy element emitowany przez źródło Observable i emituje zmodyfikowany element.

  • FlatMap, SwitchMap i ConcatMap również stosują funkcję do każdego emitowanego elementu, ale zamiast zwracać zmodyfikowany element, zwraca sam Observable, który może ponownie emitować dane.

  • Praca z FlatMap i ConcatMap jest prawie taka sama. Scalają elementy emitowane przez wiele Observables i zwracają jeden Observable.

  • Różnica między FlatMap i ConcatMap polega na kolejności, w jakiej elementy są emitowane.
  • FlatMap może przeplatać elementy podczas emisji, tzn. Kolejność emitowanych elementów nie jest zachowywana.
  • ConcatMap zachowuje kolejność elementów. Ale główną wadą ConcatMap jest to, że musi czekać, aż każdy Observable zakończy swoją pracę, dlatego asynchroniczność nie jest utrzymywana.
  • SwitchMap różni się nieco od FlatMap i ConcatMap . SwitchMap wypisuje się z poprzedniego źródła Observable za każdym razem, gdy zaczął emitować nowy element, w ten sposób zawsze emitując elementy z bieżącego Observable.

1

Jeśli szukasz przykładowego kodu

/**
 * We switch from original item to a new observable just using switchMap.
 * It´s a way to replace the Observable instead just the item as map does
 * Emitted:Person{name='Pablo', age=0, sex='no_sex'}
 */
@Test
public void testSwitchMap() {
    Observable.just(new Person("Pablo", 34, "male"))
              .switchMap(person -> Observable.just(new Person("Pablo", 0, "no_sex")))
              .subscribe(System.out::println);

}

Możesz zobaczyć więcej przykładów tutaj https://github.com/politrons/reactive


4
Ale tęsknisz za kluczową cechą switchMap, która odróżnia go od flatMap - tylko najnowsze Observable mają znaczenie, podczas wypisywania się z poprzednich.
Artem Novikov

3
W tym przykładzie, po wymianie switchMapze flatMapto będzie działać dokładnie tak samo.
Piotr Wittchen

1

Oto jeszcze jeden przykład o długości 101 linii . To wszystko wyjaśnia.

Jak powiedziano: dostaje ostatnią obserwowalną (najwolniejszą, jeśli wolisz) i ignoruje resztę.

W rezultacie:

Time | scheduler | state
----------------------------
0    | main      | Starting
84   | main      | Created
103  | main      | Subscribed
118  | Sched-C-0 | Going to emmit: A
119  | Sched-C-1 | Going to emmit: B
119  | Sched-C-0 | Sleep for 1 seconds for A
119  | Sched-C-1 | Sleep for 2 seconds for B
1123 | Sched-C-0 | Emitted (A) in 1000 milliseconds
2122 | Sched-C-1 | Emitted (B) in 2000 milliseconds
2128 | Sched-C-1 | Got B processed
2128 | Sched-C-1 | Completed

Widzisz, A zostało zignorowane.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.