RxJava provides various operators to transform items emitted by an observable into other observables. Two of the most popular operators are flatMap and switchMap. The difference between the two is often hard to understand for beginners in reactive programming.
For an introduction to RxJava, refer to this article.
In this tutorial, we'll understand the difference by walking through a simple example.
The flatMap operator converts each item returned from a source observable into an independent observable using the supplied function and then merges all the observables into a single observable. The order in which observables are merged isn't guaranteed to be the same as in the source Observable.
Let's take a search engine as an example. Consider that we want to display search results immediately after we type in each character of the word:
For simplicity's sake, we have taken the search query input as a list of words.
Also, we always return two search results for each word.
// 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"));
Please note that the order isn't always the same with every run.
The switchMap operator is similar to flatMap, except that it retains the result of only the latest observable, discarding the previous ones.
Let's change our requirement in that we want to get search results for only the final fully-formed word (in this case, “books”) and not for the partial query strings. To achieve this, we can use switchMap.
If we just replace flatMap with switchMap in the above code example, the following assertions would be valid:
assertEquals(2, actualOutput.size()); assertThat(actualOutput, hasItems("books FirstResult", "books SecondResult"));
As we see here, we only got a single observable containing the latest input item from the source observable. All previous results were discarded.
To summarize, switchMap differs from flatMap in that it only retains the output of applying a provided function to the latest item emitted by the source Observable, flatMap, on the other hand, retains all results and returns them in an interleaved manner without guaranteeing order.
As always, the code used in this article is available over on GitHub.