Aggregate runtime exceptions in Java 8 streams - java

Let's say I have a method which throws a runtime exception. I'm using a Stream to call this method on items in a list.
class ABC {
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
public void doStuffOnList(List<MyObject> myObjs) {
try {
myObjs.stream().forEach(ABC:doStuff);
} catch(AggregateRuntimeException??? are) {
...
}
}
}
Now I want all items in the list to be processed, and any runtime exceptions on individual items to be collected into an "aggregate" runtime exception which will be thrown at the end.
In my real code, I am making 3rd party API calls which may throw runtime exceptions. I want to make sure that all items are processed and any errors reported at the end.
I can think of a few ways to hack this out, such as a map() function which catches and returns the exception (..shudder..). But is there a native way to do this? If not, is there another way to implement it cleanly?

In this simple case where the doStuff method is void and you only care about the exceptions, you can keep things simple:
myObjs.stream()
.flatMap(o -> {
try {
ABC.doStuff(o);
return null;
} catch (RuntimeException ex) {
return Stream.of(ex);
}
})
// now a stream of thrown exceptions.
// can collect them to list or reduce into one exception
.reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw ex;
});
However, if your requirements are more complicated and you prefer to stick with the standard library, CompletableFuture can serve to represent "either success or failure" (albeit with some warts):
public static void doStuffOnList(List<MyObject> myObjs) {
myObjs.stream()
.flatMap(o -> completedFuture(o)
.thenAccept(ABC::doStuff)
.handle((x, ex) -> ex != null ? Stream.of(ex) : null)
.join()
).reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw new RuntimeException(ex);
});
}

There are already some implementations of Try monad for Java. I found better-java8-monads library, for example. Using it, you can write in the following style.
Suppose you want to map your values and track all the exceptions:
public String doStuff(String s) {
if(s.startsWith("a")) {
throw new IllegalArgumentException("Incorrect string: "+s);
}
return s.trim();
}
Let's have some input:
List<String> input = Arrays.asList("aaa", "b", "abc ", " qqq ");
Now we can map them to successful tries and pass to your method, then collect successfully handled data and failures separately:
Map<Boolean, List<Try<String>>> result = input.stream()
.map(Try::successful).map(t -> t.map(this::doStuff))
.collect(Collectors.partitioningBy(Try::isSuccess));
After that you can process successful entries:
System.out.println(result.get(true).stream()
.map(t -> t.orElse(null)).collect(Collectors.joining(",")));
And do something with all the exceptions:
result.get(false).stream().forEach(t -> t.onFailure(System.out::println));
The output is:
b,qqq
java.lang.IllegalArgumentException: Incorrect string: aaa
java.lang.IllegalArgumentException: Incorrect string: abc
I personally don't like how this library is designed, but probably it will be suitable for you.
Here's a gist with complete example.

Here's a variation on the theme of mapping-to-exceptions.
Start with your existing doStuff method. Note that this conforms to the functional interface Consumer<MyObject>.
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
Now write a higher-order function that wraps this and turns this into a function that might or might not return an exception. We want to call this from flatMap, so the way "might or might not" is expressed is by returning a stream containing the exception or an empty stream. I'll use RuntimeException as the exception type here, but of course it could be anything. (In fact it might be useful to use this technique with checked exceptions.)
<T> Function<T,Stream<RuntimeException>> ex(Consumer<T> cons) {
return t -> {
try {
cons.accept(t);
return Stream.empty();
} catch (RuntimeException re) {
return Stream.of(re);
}
};
}
Now rewrite doStuffOnList to use this within a stream:
void doStuffOnList(List<MyObject> myObjs) {
List<RuntimeException> exs =
myObjs.stream()
.flatMap(ex(this::doStuff))
.collect(Collectors.toList());
System.out.println("Exceptions: " + exs);
}

The only possible way I can imagine is to map values in a list to a monad, that will represent the result of your processing execution (either success with value or failure with throwable). And then fold your stream into single result with aggregated list of values or one exception with list of suppressed ones from the previous steps.
public Result<?> doStuff(List<?> list) {
return list.stream().map(this::process).reduce(RESULT_MERGER)
}
public Result<SomeType> process(Object listItem) {
try {
Object result = /* Do the processing */ listItem;
return Result.success(result);
} catch (Exception e) {
return Result.failure(e);
}
}
public static final BinaryOperator<Result<?>> RESULT_MERGER = (left, right) -> left.merge(right)
Result implementation may vary but I think you get the idea.

Related

Project Reactor: Designing a reactive API

I have a map function which defined as follows: Mono<OUT> map(IN in)
Here's a concrete example:
public Mono<Integer> map(String s) {
return Mono.fromCallable(() -> {
try {
Thread.sleep(1_000); // simulate HTTP request
return 1;
} catch (Exception e) {}
return -1; // need to return something.
});
}
The problem is that in case of an error (i.e. IOException), we still need to return some output. There's also a possibility that there might no be an answer (but no error occurred)
One solution could be an Optional::empty but I think it's cumbersome. Preferably, I'd like to return Mono::empty if an error occurred.
The reason is, Mono::empty gets consumed by the subscriber without any further handling. Here's an example:
Flux.just(
Mono.just("123"),
Mono.empty(),
Mono.just("456")
).flatMap(s -> s)
.subscribe(System.out::println);
The output would be:
123
456
How can achieve the same behaviour?
What should map look like?
EDIT:
Rethinking it, maybe I better off return some container (like Optional) or a custom one (Result) which can be empty.
If I understand correctly, here's what you need:
return Mono.fromCallable(() -> {
Thread.sleep(1_000); // simulate HTTP request
return 1;
}).onErrorResume(_ -> Mono.empty())

Rewriting an if statement which throws an exception in a cleaner way

Let's suppose we have an if statement like this:
public A save(A a) {
if (isValid.test(a)) {
return aRepository.save(a);
}
throw new ANotValidException("A is not valid");
}
isValid is a Predicate and it may look like:
private Predicate<A> isValid = (a) -> (a != null);
What do you think? Can I make it cleaner somehow?
I mean, for example using an Optional to reduce it in 1 line with an .orElseThrow();
A more precise version using Optional and throwing a custom Exception shall be :
public A save(A a) throws ANotValidException { // throws the custom exception
return Optional.ofNullable(a) // since your predicate is to check for not null
.map(aRepository::save)
.orElseThrow(() -> new ANotValidException(a + "A is not valid"));
}
An Optional can make the code more readable, particularly around the use of your predicate object:
public A save(A a) {
return Optional.ofNullable(a)
.filter(isValid)
.map(aRepository::save)
.orElseThrow(() -> new ANotValidException("A is not valid"));
}
You can also get rid of the predicate altogether as it's simple enough to use Objects::nonNull (unless your real predicate's test is more complex). And in that case, keeping your current condition checks would probably make more sense (in my opinion).
One could argue that it would be more natural to read it in the opposite order, that is first handle the validation and the result of it and then move on to saving the object.
public A save(A a) {
if (!isValid.test(a)) {
throw new ANotValidException("A is not valid");
}
return aRepository.save(a);
}

Helper method that returns a thing, or causes a return in the calling scope / context

I can't figure out how to factor out this code.
private CompletionStage<Response<String>> foo(RequestContext rc) {
final Optional<String> campaignIdOpt = rc.request().parameter("campaignId").filter(s -> !s.isEmpty());
final Optional<String> creativeIdOpt = rc.request().parameter("creativeId").filter(s -> !s.isEmpty());
Optional<Uuid> campaignIdOptOfUuid = Optional.empty();
if (campaignIdOpt.isPresent()) {
try {
campaignIdOptOfUuid = Optional.of(UuidUtils.fromString(campaignIdOpt.get()));
} catch (IllegalArgumentException e) {
LOG.error(String.format("Invalid campaignId: %s", campaignIdOpt.get()), e);
return CompletableFuture.completedFuture(
Response.forStatus(Status.BAD_REQUEST.withReasonPhrase("Invalid campaignId provided.")));
}
}
Optional<Uuid> creativeIdOptOfUuid = Optional.empty();
if (creativeIdOpt.isPresent()) {
try {
creativeIdOptOfUuid = Optional.of(UuidUtils.fromString(creativeIdOpt.get()));
} catch (IllegalArgumentException e) {
LOG.error(String.format("Invalid creativeId: %s", creativeIdOpt.get()), e);
return CompletableFuture.completedFuture(
Response.forStatus(Status.BAD_REQUEST.withReasonPhrase("Invalid creativeId provided.")));
}
}
// Simplified, do something with Uuids.
return bar(campaignIdOptOfUuid, creativeIdOptOfUuid);
}
Basically, we very frequently need to parse Google protobuf Uuids from a query string to pass on to another service that will find (or not find). We need to pass along an empty optional if a parameter was not set or an empty string, as both cases mean, "Don't filter by this parameter." Finally, if the string doesn't parse at all, then we want to immediately return an error 400 (Bad Request), rather than pass along a non-sense param to the service.
So, codewise, I want a utility method that
takes an Optional<String>, and
returns an Optional<Uuid> if present, Optional.empty() otherwise, and
if an exception is thrown, return an error from the original context.
But obviously, I can't "double-return." What pattern do I use to achieve this though? I tried to create an encapsulator for both an Optional<Uuid> and a CompletionStage<Response<String>> but it was awkward. Is there some idiomatic way of doing this?
You can use a loop. A loop allows you to handle all elements equally, thus removing the code duplication, while still allowing to return immediately:
private CompletionStage<Response<String>> foo(RequestContext rc) {
String[] parameters = {"campaignId", "creativeId" };
List<Optional<Uuid>> uuids = new ArrayList<>(parameters.length);
for(String param: parameters) {
Optional<String> o1 = rc.request().parameter(param).filter(s -> !s.isEmpty());
Optional<Uuid> o2;
try {
o2 = o1.map(UuidUtils::fromString);
} catch(IllegalArgumentException e) {
LOG.error(String.format("Invalid %s: %s", param, o1.get()), e);
return CompletableFuture.completedFuture(
Response.forStatus(Status.BAD_REQUEST
.withReasonPhrase("Invalid "+param+ " provided.")));
}
uuids.add(o2);
}
// Simplified, do something with Uuids.
return bar(uuids.get(0), uuids.get(1));
}
Otherwise, you would need to create a method returning an object holding two alternative results (like Either); the JDK does not provide such a type yet. A method could simply throw on an erroneous condition but that would bring you back to square one when the common code is mostly the exception handling.
Note that calling Optional.map on an empty optional will already return an empty optional, without evaluating the provided function, so you don’t need to check via ifPresent, etc.

Correct lambda filter implementation

I have a case where I need to
map an object, if the mapping function throws an exception, I map it to null.
filter the mapped stream for null object, if null then throw Exception, else collect to List.
How would I achieve this?
list.stream().map(ob-> {
try {
// cannot throw only catch
return function(ob);
} catch (Exception e) {
log.error(e);
return null;
}
}).filter(Objects::isNull).findFirst().orElseThrow(Exception::new);
Now my question is how should I tweak/refactor the above lambda to throw new Exception() on null or else collect(Collectors.toList()).
If you intend to report the exception (which is a good idea), you should never map it to null in the first place. Since certain functional interfaces do not allow to throw a checked exception, you should rethrow it wrapped in an unchecked exception:
try {
List<Object> result = list.stream().map(ob-> {
try {
// cannot throw checked exception types
return function(ob);
} catch(Exception e) {
throw new CompletionException(e);
}
}).collect(Collectors.toList());
} catch(CompletionException ex) {
throw (Exception)ex.getCause();
}
The key point is that this will throw the original exception, with all information contained within it, instead of creating a new instance via new Exception() that would contain no information about the cause at all.
Note that for some cases, there are already dedicated exception types, e.g. UncheckedIOException to wrap an IOException. In other cases, it might be cleaner to declare your own unchecked exception type, to be sure that it doesn’t get mixed up with other exceptions thrown by other components of your application.
You can partition by a predicate and throw an exception if the map contains a non-empty collection for the null key:
Map<Boolean, List<String>> resultMap = list.stream().map(ob-> {
try {
return function(ob);
} catch (Exception e) {
return null;
}
}).collect(Collectors.partitioningBy(Objects::isNull));
if(!resultMap.get(Boolean.TRUE).isEmpty()) {
throw new Exception();
}
return resultMap.get(Boolean.FALSE);
Collectors.partitioningBy(Objects::isNull) will return a Map<Boolean, List<T>> where true will be mapped to a list with all elements that matched the predicate (Objects::isNull), and false to those that didn't.
If the true collection is not empty, you know you can raise the exception.
I would throw a exception and leave right now the stream processing if I detect that I don't need to iterate next elements. Why going on performing logic if it is helpless ?
So I would not use built-in map() in this case and not stream either.
I think that it would make things very readable by introducing a simple method to do the mapping :
try{
return map(list);
}
catch (Exception e) {
throw new AnyExceptionYouWant(e);
}
// helper method
List<Bar> map (List<Foo> list) throws Exception{
List<Bar>> bars = new ArrayList<>();
for (Foo foo : list){
bars.add(function(foo));
}
return bars;
}
If you want to use readable and easy to maintain streams, you should probably not throw any exception in function(). You could for example return a List of Optionals and so it would be simple to handle the empty case in your stream.
Well, there is possible to work with try-catch clauses inside lambdas, yet it's not recommended since the lambdas should stay as short as possible.
Separate the mapper to the new method and call it in the lambda instead.
private static final <T, R> R tryMapOrElseNull(T t) {
try {
return function(t);
} catch (Exception e) {
this.log.error(e);
return null;
}
}
Then use the method as a method reference in the Stream::map method. Firstly, collect the newly mapped elements and then just simply check for null.
newList = list.stream().map(MyClass::safeMap).collect(Collectors.toList());
if (newList.contains(null)) {
throw new Exception();
}
I'd do this in two steps, first collect to a list:
List<T> result = list.stream().map(ob -> {
try {
// cannot throw only catch, since lambda expression
return function(ob);
} catch (Exception e) {
log.error(e);
return null;
}
}).collect(toList());
where T is the type of elements being mapped to.
then check for nullity:
if(result.contains(null)) {/* throw exeception... */}
else { /* do something else */}

When do you use map vs flatMap in RxJava?

When do you use map vs flatMap in RxJava?
Say, for example, we want to map Files containing JSON into Strings that contain the JSON--
Using map, we have to deal with the Exception somehow. But how?:
Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// So Exception. What to do ?
}
return null; // Not good :(
}
});
Using flatMap, it's much more verbose, but we can forward the problem down the chain of Observables and handle the error if we choose somewhere else and even retry:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(final File file) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override public void call(Subscriber<? super String> subscriber) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
subscriber.onNext(json);
subscriber.onCompleted();
} catch (FileNotFoundException e) {
subscriber.onError(e);
}
}
});
}
});
I like the simplicity of the map, but the error handling of flatmap (not the verbosity). I haven't seen any best practices on this floating around and I'm curious how this is being used in practice.
map transform one event to another.
flatMap transform one event to zero or more event. (this is taken from IntroToRx)
As you want to transform your json to an object, using map should be enough.
Dealing with the FileNotFoundException is another problem (using map or flatmap wouldn't solve this issue).
To solve your Exception problem, just throw it with a Non checked exception : RX will call the onError handler for you.
Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// this exception is a part of rx-java
throw OnErrorThrowable.addValueAsLastCause(e, file);
}
}
});
the exact same version with flatmap :
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(File file) {
try {
return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
} catch (FileNotFoundException e) {
// this static method is a part of rx-java. It will return an exception which is associated to the value.
throw OnErrorThrowable.addValueAsLastCause(e, file);
// alternatively, you can return Obersable.empty(); instead of throwing exception
}
}
});
You can return too, in the flatMap version a new Observable that is just an error.
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(File file) {
try {
return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
} catch (FileNotFoundException e) {
return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file));
}
}
});
FlatMap behaves very much like map, the difference is that the function it applies returns an observable itself, so it's perfectly suited to map over asynchronous operations.
In the practical sense, the function Map applies just makes a transformation over the chained response (not returning an Observable); while the function FlatMap applies returns an Observable<T>, that is why FlatMap is recommended if you plan to make an asynchronous call inside the method.
Summary:
Map returns an object of type T
FlatMap returns an Observable.
A clear example can be seen here: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk .
Couchbase Java 2.X Client uses Rx to provide asynchronous calls in a convenient way. Since it uses Rx, it has the methods map and FlatMap, the explanation in their documentation might be helpful to understand the general concept.
To handle errors, override onError on your susbcriber.
Subscriber<String> mySubscriber = new Subscriber<String>() {
#Override
public void onNext(String s) { System.out.println(s); }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
};
It might help to look at this document: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
A good source about how to manage errors with RX can be found at: https://gist.github.com/daschl/db9fcc9d2b932115b679
In your case you need map, since there is only 1 input and 1 output.
map - supplied function simply accepts an item and returns an item which will be emitted further (only once) down.
flatMap - supplied function accepts an item then returns an "Observable", meaning each item of the new "Observable" will be emitted separately further down.
May be code will clear things up for you:
Observable.just("item1").map( str -> {
System.out.println("inside the map " + str);
return str;
}).subscribe(System.out::println);
Observable.just("item2").flatMap( str -> {
System.out.println("inside the flatMap " + str);
return Observable.just(str + "+", str + "++" , str + "+++");
}).subscribe(System.out::println);
Output:
inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++
The question is When do you use map vs flatMap in RxJava?. And I think a simple demo is more specific.
When you want to convert item emitted to another type , in your case converting file to String, map and flatMap can both work. But I prefer map operator because it's more clearly.
However in some place, flatMap can do magic work but map can't. For example, I want to get a user's info but I have to first get his id when user login in. Obviously I need two requests and they are in order.
Let's begin.
Observable<LoginResponse> login(String email, String password);
Observable<UserInfo> fetchUserInfo(String userId);
Here are two methods, one for login returned Response, and another for fetching user info.
login(email, password)
.flatMap(response ->
fetchUserInfo(response.id))
.subscribe(userInfo -> {
// get user info and you update ui now
});
As you see, in function flatMap applies, at first I get user id from Response then fetch user info. When two requests are finished, we can do our job such as updating UI or save data into database.
However if you use map you can't write such nice code. In a word, flatMap can help us serialize requests.
The way I think about it is that you use flatMap when the function you wanted to put inside of map() returns an Observable. In which case you might still try to use map() but it would be unpractical. Let me try to explain why.
If in such case you decided to stick with map, you would get an Observable<Observable<Something>>. For example in your case, if we used an imaginary RxGson library, that returned an Observable<String> from it's toJson() method (instead of simply returning a String) it would look like this:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
#Override public Observable<String>> call(File file) {
return new RxGson().toJson(new FileReader(file), Object.class);
}
}); // you get Observable<Observable<String>> here
At this point it would be pretty tricky to subscribe() to such an observable. Inside of it you would get an Observable<String> to which you would again need to subscribe() to get the value. Which is not practical or nice to look at.
So to make it useful one idea is to "flatten" this observable of observables (you might start to see where the name _flat_Map comes from). RxJava provides a few ways to flatten observables and for sake of simplicity lets assume merge is what we want. Merge basically takes a bunch of observables and emits whenever any of them emits. (Lots of people would argue switch would be a better default. But if you're emitting just one value, it doesn't matter anyway.)
So amending our previous snippet we would get:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
#Override public Observable<String>> call(File file) {
return new RxGson().toJson(new FileReader(file), Object.class);
}
}).merge(); // you get Observable<String> here
This is a lot more useful, because subscribing to that (or mapping, or filtering, or...) you just get the String value. (Also, mind you, such variant of merge() does not exist in RxJava, but if you understand the idea of merge then I hope you also understand how that would work.)
So basically because such merge() should probably only ever be useful when it succeeds a map() returning an observable and so you don't have to type this over and over again, flatMap() was created as a shorthand. It applies the mapping function just as a normal map() would, but later instead of emitting the returned values it also "flattens" (or merges) them.
That's the general use case. It is most useful in a codebase that uses Rx allover the place and you've got many methods returning observables, which you want to chain with other methods returning observables.
In your use case it happens to be useful as well, because map() can only transform one value emitted in onNext() into another value emitted in onNext(). But it cannot transform it into multiple values, no value at all or an error. And as akarnokd wrote in his answer (and mind you he's much smarter than me, probably in general, but at least when it comes to RxJava) you shouldn't throw exceptions from your map(). So instead you can use flatMap() and
return Observable.just(value);
when all goes well, but
return Observable.error(exception);
when something fails.
See his answer for a complete snippet: https://stackoverflow.com/a/30330772/1402641
Here is a simple thumb-rule that I use help me decide as when to use flatMap() over map() in Rx's Observable.
Once you come to a decision that you're going to employ a map transformation, you'd write your transformation code to return some Object right?
If what you're returning as end result of your transformation is:
a non-observable object then you'd use just map(). And map() wraps that object in an Observable and emits it.
an Observable object, then you'd use flatMap(). And flatMap() unwraps the Observable, picks the returned object, wraps it with its own Observable and emits it.
Say for example we've a method titleCase(String inputParam) that returns Titled Cased String object of the input param. The return type of this method can be String or Observable<String>.
If the return type of titleCase(..) were to be mere String, then you'd use map(s -> titleCase(s))
If the return type of titleCase(..) were to be Observable<String>, then you'd use flatMap(s -> titleCase(s))
Hope that clarifies.
I just wanted to add that with flatMap, you don't really need to use your own custom Observable inside the function and you can rely on standard factory methods/operators:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(final File file) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
return Observable.just(json);
} catch (FileNotFoundException ex) {
return Observable.<String>error(ex);
}
}
});
Generally, you should avoid throwing (Runtime-) exceptions from onXXX methods and callbacks if possible, even though we placed as many safeguards as we could in RxJava.
In that scenario use map, you don't need a new Observable for it.
you should use Exceptions.propagate, which is a wrapper so you can send those checked exceptions to the rx mechanism
Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
throw Exceptions.propagate(t); /will propagate it as error
}
}
});
You then should handle this error in the subscriber
obs.subscribe(new Subscriber<String>() {
#Override
public void onNext(String s) { //valid result }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { //e might be the FileNotFoundException you got }
};);
There is an excellent post for it: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
RxJava Map vs FlatMap
They both are Transforming operators but map has 1-1 relation and flatMap has 1-0 or many relation.
map and flatmap emits stream with
map- only 1 element
flatmap - 0/many elements
map emits single element and flatmap emits a stream of elements
Map operator
map(new Function<A, B>() {
#Override
public B apply(A a) throws Exception {
B b = new B(a);
return b;
}
})
FlatMap operator
flatMap(new Function<A, ObservableSource<B>>() {
#Override
public ObservableSource<B> apply(A a) throws Exception {
return foo(a);
}
})
[flatMap vs concatMap]
[Swift map vs flatMap]
In some cases you might end up having chain of observables, wherein your observable would return another observable. 'flatmap' kind of unwraps the second observable which is buried in the first one and let you directly access the data second observable is spitting out while subscribing.
Flatmap maps observables to observables.
Map maps items to items.
Flatmap is more flexible but Map is more lightweight and direct, so it kind of depends on your usecase.
If you are doing ANYTHING async (including switching threads), you should be using Flatmap, as Map will not check if the consumer is disposed (part of the lightweight-ness)

Categories

Resources