«1. Обзор

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

Для ознакомления с RxJava обратитесь к этой статье.

В этом уроке мы поймем разницу на простом примере.

2. flatMap

Оператор flatMap преобразует каждый элемент, возвращенный из исходного наблюдаемого объекта, в независимый наблюдаемый объект с помощью предоставленной функции, а затем объединяет все наблюдаемые объекты в один наблюдаемый объект. Порядок, в котором объединяются наблюдаемые, не обязательно будет таким же, как в исходном наблюдаемом.

Возьмем в качестве примера поисковую систему. Учтите, что мы хотим отображать результаты поиска сразу же после ввода каждого символа слова:

Для простоты мы приняли ввод поискового запроса в виде списка слов.

Кроме того, мы всегда возвращаем два результата поиска для каждого слова.

// given
List<String> actualOutput = new ArrayList<>();
TestScheduler scheduler = new TestScheduler();
List<String> keywordToSearch = Arrays.asList("b", "bo", "boo", "book", "books");

// when
Observable.fromIterable(keywordToSearch)
  .flatMap(s -> Observable.just(s + " FirstResult", s + " SecondResult")
    .delay(10, TimeUnit.SECONDS, scheduler))
  .toList()
  .doOnSuccess(s -> actualOutput.addAll(s))
  .subscribe();

scheduler.advanceTimeBy(1, TimeUnit.MINUTES);

// then
assertThat(actualOutput, hasItems("b FirstResult", "b SecondResult",
  "boo FirstResult", "boo SecondResult",
  "bo FirstResult", "bo SecondResult",
  "book FirstResult", "book SecondResult",
  "books FirstResult", "books SecondResult"));

Обратите внимание, что порядок не всегда одинаков при каждом запуске.

3. switchMap

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

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

Если мы просто заменим flatMap на switchMap в приведенном выше примере кода, следующие утверждения будут верны:

assertEquals(2, actualOutput.size());
assertThat(actualOutput, hasItems("books FirstResult", "books SecondResult"));

Как мы видим здесь, мы получили только один наблюдаемый объект, содержащий последний входной элемент из исходного наблюдаемого объекта. Все предыдущие результаты были отброшены.

4. Заключение

Подводя итог, можно сказать, что switchMap отличается от flatMap тем, что сохраняет только вывод применения предоставленной функции к последнему элементу, испускаемому исходным Observable, а flatMap, с другой стороны, сохраняет все результаты и возвращает их в чередующемся порядке без гарантии порядка.

Как всегда, код, используемый в этой статье, доступен на GitHub.