В чем разница между flatmap и switchmap в RxJava?

149

Rxjava док определение switchmap является довольно расплывчатым и ссылки на той же странице , как flatmap. В чем разница между двумя операторами?

Джулиан Го
источник
1
Об этом ссылки на той же странице, что и плоская карта . Это действительно правда. Но прокрутите вниз до раздела « Информация о языке » и откройте интересный оператор. Я думаю, что это должно быть сделано автоматически из TOC, но ... Также вы можете увидеть ту же картину в Javadoc .
Руслан Стельмаченко

Ответы:

180

Согласно документации ( http://reactivex.io/documentation/operators/flatmap.html )

switchMapподобно flatMap, но он будет издавать только элементы из новых наблюдаемых , пока новое событие не излучается из источника наблюдаемого.

Мраморная диаграмма хорошо это показывает. Обратите внимание на разницу в диаграммах:

Во switchMapвтором исходном излучении ( зеленый мрамор ) не испускается его второе сопоставленное излучение ( зеленый квадрат ), поскольку третье исходное излучение ( синий мрамор ) уже началось и уже выпустило свое первое сопоставленное излучение ( синий ромб ). Другими словами, происходит только первое из двух нанесенных на карту зеленых выбросов; зеленый квадрат не испускается, потому что синий бриллиант побеждает его.

В flatMap, будут отображены все сопоставленные результаты, даже если они «устаревшие». Другими словами, как первый и второй из отображенных зеленых выбросов произойдет - это зеленый квадрат бы уже вылетать (если они использовали последовательную функцию карты, так как они не сделали, вы увидите второй зеленый алмаз, даже если он излучается после первый голубой бриллиант)

switchMap в switchMap, если исходная наблюдаемая излучает что-то новое, предыдущие выбросы больше не производят отображаемые наблюдаемые;  это эффективный способ избежать устаревших результатов

flatMap

в switchMap, если исходная наблюдаемая излучает что-то новое, предыдущие выбросы больше не производят отображаемые наблюдаемые;  это эффективный способ избежать устаревших результатов

dwursteisen
источник
4
Спасибо, диаграмма очень полезна. Знаете ли вы пример из реальной жизни, где будет использоваться switchMap?
Джулиан Го
1
@JulianGo здесь есть пример: github.com/samuelgruetter/rx-playground/blob/master/… Используется .map(func).switch, но это то же самое, что и .switchMap(func).
Сэмюэль Грютер из
2
На всякий случай, если кому-то все еще нужен реальный пример switchMap, он может перейти по этой ссылке, и он поймет, когда использовать swicthMap вместо flatMap.
Германнович
2
Для примера использования SwitchMap от Бен Леша с использованием RxJs5 - смотрите минуты 25-26 здесь - youtube.com/watch?v=3LKMwkuK0ZE для меня, плоская карта уже была понятна ...
arcseldon
7
Мраморная диаграмма это хорошо показывает? Какой? Я думаю, если вы уже поняли, возможно, switchmap.
Хельзгейт
166

Я сталкивался с этим при реализации «мгновенного поиска» - то есть когда пользователь вводит текстовое поле, и результаты отображаются почти в реальном времени с каждым нажатием клавиши. Решение, кажется, заключается в следующем:

  1. Есть тема, например, PublishSubject of String
  2. В текстовом поле изменить обратный вызов, вызвать .onNext (текст)
  3. применить фильтр .debounce для ограничения скорости запросов к серверу
  4. применить .switchMap для выполнения запроса к серверу - взять поисковый запрос и вернуть Observable of SearchResponse
  5. применить .subscribe с методом, который использует SearchResponse и обновляет пользовательский интерфейс.

С flatMap результаты поиска могут устареть, потому что ответы могут возвращаться не в порядке. Чтобы исправить это, следует использовать switchMap, так как он гарантирует, что старая наблюдаемая отписывается после того, как предоставлена ​​более новая.

Таким образом, в итоге, flatMap следует использовать, когда все результаты имеют значение, независимо от их времени, и switchMap следует использовать, когда только результаты из последнего наблюдаемого значения.

user4698855
источник
Вы можете проверить этот пример в GitHub
Cabezas
95

Ни одно обсуждение flatMap не будет полным без сравнения и противопоставления switchMap, concatMapи concatMapEager.

Все эти методы принимают a, Func1который преобразует поток в Observables, которые затем испускаются; разница заключается в том, когда возвращенные Observables подписаны и отписаны, и если и когда эти выбросы этих Observables испускаются соответствующим ____Mapоператором.

  • flatMapподписывается на максимально возможное количество Observables. (Это число зависит от платформы. Например, более низкое число на Android) Используйте это, когда порядок не важен, и вы хотите выбросы как можно скорее.
  • concatMapподписывается на первый Observableи подписывается только на следующий, Observableкогда предыдущий завершен. Используйте это, когда порядок важен, и вы хотите сохранить ресурсы. Прекрасный пример - отложить сетевой вызов, проверив сначала кеш. Обычно это может сопровождаться .first()или, .takeFirst()чтобы избежать ненужной работы.

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

  • concatMapEagerработает почти так же, но подписывается на максимально возможное количество (зависит от платформы), но издает только после завершения предыдущего Observable. Идеально, когда у вас много параллельной обработки, которую нужно выполнить, но (в отличие от flatMap) вы хотите сохранить исходный порядок.

  • switchMapподпишется на последнюю Observableвстречу и отписаться от всех предыдущих Observableс. Это идеально подходит для случаев, подобных поисковым предложениям: как только пользователь изменил свой поисковый запрос, старый запрос больше не представляет интереса, поэтому он отменяется, и конечная точка Api с хорошим поведением отменяет сетевой запрос.

Если вы возвращаете Observables, которые не являются subscribeOnдругим потоком, все вышеперечисленные методы могут вести себя примерно одинаково. Интересное и полезное поведение возникает, когда вы позволяете вложенным Observableэлементам действовать в своих собственных потоках. Тогда вы можете получить получить много преимуществ от параллельной обработки, и разумно отписки или не подписываться от ObservableS , которые не интересуют ваши SubscriberS

  • ambтакже может представлять интерес. При любом количестве Observables он испускает те же элементы, что и первый, Observableиспускающий что-либо. Это может быть полезно, когда у вас есть несколько источников, которые могут / должны возвращать одно и то же, и вы хотите повысить производительность. например, сортировка, вы можете ambбыстро сортировать с помощью сортировки слиянием и использовать тот, который был быстрее.
Эндрю Галлаш
источник
1
If you are returning Observables that don't subscribeOn another thread, all of the above methods may behave much the same.- каждое объяснение, с которым switchMap vs flatMapя сталкивался раньше, упускало этот важный аспект, теперь все проясняется. Спасибо.
Энди Рез
55

switchMap когда-то назывался flatMapLatest в RxJS 4.

Он в основном просто передает события из последней Observable и отписывается от предыдущего.

Sentenza
источник
@EpicPandaForce Несмотря на то, что он несовместим с combLatest, которое выдает последние значения всякий раз, когда исходный источник наблюдает (не излучает один раз).
Майкл Фрай
2
Отчасти причина, по которой он называется switchMap, заключается в том, что вы можете реализовать этот оператор самостоятельно, используя o.map (...). Switch (). Хотя тогда я представляю, что это будет mapSwitch, который, кажется, не так легко скатывается с языка.
Найл
7

Map, FlatMap, ConcatMap и SwitchMap применяют функцию или изменяют данные, испускаемые Observable.

  • Карта изменяет каждый элемент, испускаемый наблюдаемой источником, и испускает измененный элемент.

  • FlatMap, SwitchMap и ConcatMap также применяют функцию к каждому испускаемому элементу, но вместо возврата измененного элемента он возвращает сам Observable, который может снова выдавать данные.

  • Работа FlatMap и ConcatMap практически одинакова. Они объединяют элементы, испускаемые несколькими наблюдаемыми, и возвращают одну наблюдаемую.

  • Разница между FlatMap и ConcatMap заключается в порядке, в котором отправляются элементы.
  • FlatMap может чередовать элементы во время излучения, т.е. порядок сохраняемых элементов не поддерживается.
  • ConcatMap сохраняет порядок элементов. Но главный недостаток ConcatMap заключается в том, что он должен ждать, пока каждый Observable завершит свою работу, поэтому асинхронный режим не поддерживается.
  • SwitchMap немного отличается от FlatMap и ConcatMap . SwitchMap отписывается от предыдущего источника Observable всякий раз, когда новый элемент начинает испускать, таким образом всегда испуская элементы из текущего Observable.
akhilesh0707
источник
1

Если вы ищете пример кода

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

}

Вы можете увидеть больше примеров здесь https://github.com/politrons/reactive

Павел
источник
4
Но вы упускаете ключевую особенность switchMap, которая отличает его от flatMap - только самые последние наблюдаемые вопросы при отмене подписки от предыдущих.
Артем Новиков
3
В этом примере, при замене switchMapс flatMapон будет работать точно так же.
Петр Витчен
1

Вот еще один пример - 101 строка . Это объясняет это для меня.

Как было сказано: он получает последнее наблюдаемое (самое медленное, если хотите) и игнорирует все остальное.

В следствии:

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

Вы видите, что A проигнорировали.

сес
источник