In this tutorial, we’ll be looking into three important map operators in RxJava. FlatMap, SwitchMap, ConcatMap add more power to the already powerful RxJava framework of operators.
RxJava FlatMap
A map
operator transforms each of the values from the Observable sequence.
A flatmap
operator is used to transform an observable by breaking it into smaller observables containing individual values from the first observable. Then you do stuff on the new observables and finally, the flatmap
merges these single observables into one complete observable.
The new observable is now transformed.
FlatMap flattens an observable into single observables which are modified and merged into a new observable.
The new observable that comes out of the flatMap
doesn’t guarantee you the same order. The values are interleaved.
A FlatMap emits Observables instead of values
An illustration for the RxJava documentation is given below.
What exactly does a FlatMap do?
A FlatMap divides an Observable into many singular Observables. Hence, instead of sending each of the Observable values one by one, a FlatMap does everything in parallel and then merges the results in whatever order they are completed.
Let’s look at an example of FlatMap by creating a new IntelliJ java project and adding the RxJava dependency in the build.gradle
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import rx.Observable; import rx.functions.Func1; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; public class RxOperatorMaps { public static void main(String[] args) throws InterruptedException { final List<String> race = new ArrayList<>(Arrays.asList("Alan", "Bob", "Cobb", "Dan", "Evan", "Finch")); Observable.from(race) .flatMap(new Func1<String, Observable<?>>() { @Override public Observable<?> call(String s) { final int delay = new Random().nextInt(5); return Observable.just(s).map(String::toUpperCase) .delay(delay, TimeUnit.SECONDS); } }) .subscribe(System.out::println); Thread.sleep(5000); } } //Prints //BOB //FINCH //ALAN //EVAN //COBB //DAN |
- The
flatMap
applies a function to each observable emitted. The function type is a unit of the source observable type(String in this case) and a new Observable with the type you transform it into. - In the above code, a String observable is emitted from the source which was a List of Strings observable.
- The new observables are delayed here to show you that a
flatMap
merges the single observables in the order in which they complete. - We’ve used a delay operator which delays the emission by a random number of seconds.
- We’ve set the main thread to sleep for 5 seconds to prevent the main function from returning while the Observables are still doing their work.
Note: A flatMap
implicitly contains the merge operator too.
Difference between a map and a flatMap operator
Map returns an object of type T
whereas a FlatMap returns an Observable<T>
.
Using an observable operator we can also filter down the number of values we want to reach the observer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Observable.from(race) .flatMap(s -> { final int delay = new Random().nextInt(5); if (s.contains("a")) return Observable.empty() .delay(delay, TimeUnit.SECONDS); else return Observable.just(s).map(String::toUpperCase) .delay(delay, TimeUnit.SECONDS); }) .subscribe(System.out::println); Thread.sleep(5000); //Prints //FINCH //COBB //BOB |
flatMap would be useful in android when you need to perform multiple network requests in parallel.
RxJava SwitchMap
A SwitchMap flattens the source observable but only returns the last emitted single observable.
The below illustration followed by example demonstrates what the above line means.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Observable.from(race) .switchMap((Func1<String, Observable<?>>) s -> { final int delay = new Random().nextInt(2); return Observable.just(s).map(String::toUpperCase) .delay(delay, TimeUnit.SECONDS); }) .subscribe(System.out::println); Thread.sleep(5000); //Prints for me. It can differ for you since it's random. //ALAN //EVAN //FINCH |
SwitchMap would emit only the latest observable after a particular delay. It ignores all the previous ones.
It is useful when it comes to retrieving the latest data from a feed. It’s useful when you need to identify the latest among many feeds.
RxJava concatMap
If you’ve read flatMap you almost know what a concatMap operator is. It is similar to a flatMap except that it keeps the order of the observables.
To keep the order, it needs to wait for one observable to finish before proceeding to the next, thereby breaking away from the asynchronous concept and making the task longer than usual.
Another difference between flatMap and concatMap operators
The flatMap
uses merge
operator implicitly whereas concatMap
uses concat
operator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Observable.from(race) .concatMap((Func1<String, Observable<?>>) s -> { final int delay = new Random().nextInt(4); return Observable.just(s).map(String::toUpperCase) .delay(delay, TimeUnit.SECONDS); }) .subscribe(System.out::println); Thread.sleep(5000); //Prints //ALAN //BOB //COBB |
Why did it not print all the values?
Well it took so long that the main thread woke up and exited the main method. You can increase the thread sleep time or decrease the delay random number to view all.
But you get the concept right? ConcatMap takes longer. Use it only if you need the same sequence.
Reference: RxJava docs