{"id":67846,"date":"2017-08-04T13:00:55","date_gmt":"2017-08-04T10:00:55","guid":{"rendered":"https:\/\/www.javacodegeeks.com\/?p=67846"},"modified":"2017-08-04T10:40:15","modified_gmt":"2017-08-04T07:40:15","slug":"flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html","title":{"rendered":"flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ"},"content":{"rendered":"<p>There are three, seamlessly similar operators in RxJava 2.x:\u00a0<code>flatMap()<\/code>,\u00a0<code>concatMap()<\/code>\u00a0and\u00a0<code>concatMapEager()<\/code>. All of them accept the same argument &#8211; a function from original stream&#8217;s individual item to a (sub-)stream of arbitrary type. In other words if you have a\u00a0<code>Flowable&lt;T&gt;<\/code>\u00a0you provide a function from\u00a0<code>T<\/code>\u00a0to\u00a0<code>Flowable&lt;R&gt;<\/code>\u00a0for arbitrary\u00a0<code>R<\/code>\u00a0type. After applying any of these operators you end up with\u00a0<code>Flowable&lt;R&gt;<\/code>. So how are they different?<\/p>\n<h2 id=\"sample-project\">Sample project<\/h2>\n<p>First let&#8217;s build a sample application. We will use\u00a0<a href=\"http:\/\/square.github.io\/retrofit\/\">Retrofit2<\/a>\u00a0HTTP client wrapper that has built-in plugins for RxJava2. Our task is to leverage\u00a0<a href=\"http:\/\/www.geonames.org\/export\/web-services.html\">GeoNames API<\/a>\u00a0in order to find the population of any city in the world. The interface looks as follows:<\/p>\n<pre class=\"brush:java\">public interface GeoNames {\r\n \r\n    Flowable&lt;Long&gt; populationOf(String city);\r\n \r\n}<\/pre>\n<p>The implementation of this interface is auto-generated by Retrofit, scroll down to see glue source code. For the time being just assume we have a function that takes a\u00a0<code>String<\/code>\u00a0with city name and asynchronously returns a one-element stream with a population of that city. Also assume we have a fixed stream of cities we want to look up:<\/p>\n<pre class=\"brush:java\">Flowable&lt;String&gt; cities = Flowable.just(\r\n    \"Warsaw\", \"Paris\", \"London\", \"Madrid\"\r\n);<\/pre>\n<p>Our goal is to fetch population of each city.<\/p>\n<h2 id=\"concatmap-process-upstream-sequentially\"><code>concatMap()<\/code>: process upstream sequentially<\/h2>\n<p>The sample application with\u00a0<code>concatMap()<\/code>\u00a0looks as follows:<\/p>\n<pre class=\"brush:java\">cities\r\n        .concatMap(geoNames::populationOf)\r\n        .subscribe(response -&gt; log.info(\"Population: {}\", response));<\/pre>\n<p>Before we see the outcome let&#8217;s study what\u00a0<code>concatMap()<\/code>\u00a0is doing underneath. For each upstream event (<em>city<\/em>) it invokes a function that replaces that event with a (sub)stream. In our case it&#8217;s a one-element stream of\u00a0<code>Long<\/code>\u00a0(<code>Flowable&lt;Long&gt;<\/code>). So with all operators we are comparing we end up with a stream of streams of\u00a0<code>Long<\/code>\u00a0(<code>Flowable&lt;Flowable&lt;Long&gt;&gt;<\/code>). The real difference arises when we analyze what the operator is doing in order to flatten such nested stream.<\/p>\n<p><code>concatMap()<\/code>\u00a0will first subscribe to the very first substream (<code>Flowable&lt;Long&gt;<\/code>\u00a0representing population of Warsaw). By subscribing we actually mean making the physical HTTP call. Only when the first substream completes (emits a single\u00a0<code>Long<\/code>\u00a0in our case and signals completion)\u00a0<code>concatMap()<\/code>\u00a0will continue. Continuing means subscribing to the second substream and waiting for it to complete. The resulting stream completes when the very last substream completes. This leads to a following stream: 1702139, 2138551, 7556900 and 3255944. These happen to be populations of Warsaw, Paris, London and Madrid, accordingly. The order of output is entirely predictable. However it&#8217;s also entirely sequential. No concurrency happens at all, we make second HTTP call only when the first one completed. The added complexity of RxJava doesn&#8217;t pay off at all:<\/p>\n<pre class=\"brush:bash\">23:33:33.531 | Rx-1 | --&gt; GET ...\/searchJSON?q=Warsaw http\/1.1\r\n23:33:33.656 | Rx-1 | &lt;-- 200 OK ...\/searchJSON?q=Warsaw (123ms)\r\n23:33:33.674 | Rx-1 | Population: 1702139\r\n23:33:33.676 | Rx-1 | --&gt; GET ...\/searchJSON?q=Paris http\/1.1\r\n23:33:33.715 | Rx-1 | &lt;-- 200 OK ...\/searchJSON?q=Paris (38ms)\r\n23:33:33.715 | Rx-1 | Population: 2138551\r\n23:33:33.716 | Rx-1 | --&gt; GET ...\/searchJSON?q=London http\/1.1\r\n23:33:33.754 | Rx-1 | &lt;-- 200 OK ...\/searchJSON?q=London (37ms)\r\n23:33:33.754 | Rx-1 | Population: 7556900\r\n23:33:33.755 | Rx-1 | --&gt; GET ...\/searchJSON?q=Madrid http\/1.1\r\n23:33:33.795 | Rx-1 | &lt;-- 200 OK ...\/searchJSON?q=Madrid (40ms)\r\n23:33:33.796 | Rx-1 | Population: 3255944<\/pre>\n<p>As you can see no multithreading occurs, requests are sequential, waiting for each other. Technically not all of them must happen in the same thread, but they never overlap and take advantage of concurrency. The big plus is guaranteed order of resulting events, which is not that obvious once we jump into\u00a0<code>flatMap()<\/code>&#8230;<\/p>\n<h2 id=\"flatmap-processing-results-on-the-fly-out-of-order\"><code>flatMap()<\/code>: processing results on-the-fly, out-of-order<\/h2>\n<p><code>flatMap()<\/code>\u00a0code is almost exactly the same:<\/p>\n<pre class=\"brush:java\">cities\r\n        .flatMap(geoNames::populationOf)\r\n        .subscribe(response -&gt; log.info(\"Population: {}\", response));<\/pre>\n<p>And just like before we start with a stream of streams of\u00a0<code>Long<\/code>\u00a0(<code>Flowable&lt;Flowable&lt;Long&gt;&gt;<\/code>). However rather than subscribing to each substream one after another,\u00a0<code>flatMap()<\/code>\u00a0operator eagerly subscribes to all substreams at once. This means we see multiple HTTP requests being initiated at the same time in different threads:<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\"brush:bash\">00:10:04.919 | Rx-2 | --&gt; GET ...\/searchJSON?q=Paris http\/1.1\r\n00:10:04.919 | Rx-1 | --&gt; GET ...\/searchJSON?q=Warsaw http\/1.1\r\n00:10:04.919 | Rx-3 | --&gt; GET ...\/searchJSON?q=London http\/1.1\r\n00:10:04.919 | Rx-4 | --&gt; GET ...\/searchJSON?q=Madrid http\/1.1\r\n00:10:05.449 | Rx-3 | &lt;-- 200 OK ...\/searchJSON (529ms)\r\n00:10:05.462 | Rx-3 | Population: 7556900\r\n00:10:05.477 | Rx-1 | &lt;-- 200 OK ...\/searchJSON (557ms)\r\n00:10:05.478 | Rx-1 | Population: 1702139\r\n00:10:05.751 | Rx-4 | &lt;-- 200 OK ...\/searchJSON (831ms)\r\n00:10:05.752 | Rx-4 | Population: 3255944\r\n00:10:05.841 | Rx-2 | &lt;-- 200 OK ...\/searchJSON (922ms)\r\n00:10:05.843 | Rx-2 | Population: 2138551<\/pre>\n<p>When any of the underlying substreams emit any value, it is immediately passed downstream to the subscriber. This means we can now process events on-the-fly, as they are produced. Notice that the resulting stream is out-of-order. The first event we received is 7556900, which happens to be the population of London, second in the initial stream. Contrary to\u00a0<code>concatMap()<\/code>,\u00a0<code>flatMap()<\/code>\u00a0can&#8217;t preserve order, thus emits values in &#8220;random&#8221; order. Well, not really random, we simply receive values as soon as they are available. In this particular execution HTTP response for London came first, but there is absolutely no guarantee for that. This leads to an interesting problem. We have a stream of various population values and initial stream of cities. However the output stream can be an arbitrary permutation of events and we have no idea which population corresponds to which city. We will address this problem in a subsequent article.<\/p>\n<h2 id=\"concatmapeager-concurrent-in-order-but-somewhat-expensive\"><code>concatMapEager()<\/code>: concurrent, in-order, but somewhat expensive<\/h2>\n<p><code>concatMapEager()<\/code>\u00a0seems to bring the best of both worlds: concurrency and guaranteed order of output events:<\/p>\n<pre class=\"brush:java\">cities\r\n        .concatMapEager(geoNames::populationOf)\r\n        .subscribe(response -&gt; log.info(\"Population: {}\", response));<\/pre>\n<p>After learning what\u00a0<code>concatMap()<\/code>\u00a0and\u00a0<code>flatMap()<\/code>\u00a0are doing, understanding\u00a0<code>concatMapEager()<\/code>\u00a0is fairly simple. Having stream of streams\u00a0<code>concatMapEager()<\/code>\u00a0eagerly (<em>duh!<\/em>) subscribes to all substreams at the same time, concurrently. However this operator makes sure that results from the first substream are propagated first, even if it&#8217;s not the first one to complete. An example will quickly reveal what this means:<\/p>\n<pre class=\"brush:bash\">00:34:18.371 | Rx-2 | --&gt; GET ...\/searchJSON?q=Paris http\/1.1\r\n00:34:18.371 | Rx-3 | --&gt; GET ...\/searchJSON?q=London http\/1.1\r\n00:34:18.371 | Rx-4 | --&gt; GET ...\/searchJSON?q=Madrid http\/1.1\r\n00:34:18.371 | Rx-1 | --&gt; GET ...\/searchJSON?q=Warsaw http\/1.1\r\n00:34:18.517 | Rx-3 | &lt;-- 200 OK ...\/searchJSON?q=London (143ms)\r\n00:34:18.563 | Rx-1 | &lt;-- 200 OK ...\/searchJSON?q=Warsaw (189ms)\r\n00:34:18.565 | Rx-1 | Population: 1702139\r\n00:34:20.460 | Rx-2 | &lt;-- 200 OK ...\/searchJSON?q=Paris (2086ms)\r\n00:34:20.460 | Rx-4 | &lt;-- 200 OK ...\/searchJSON?q=Madrid (2086ms)\r\n00:34:20.461 | Rx-2 | Population: 2138551\r\n00:34:20.462 | Rx-2 | Population: 7556900\r\n00:34:20.462 | Rx-2 | Population: 3255944<\/pre>\n<p>We initiate four HTTP requests instantly. From the log output we clearly see that the population of London was returned first. However the subscriber did not receive it because population of Warsaw didn&#8217;t arrive yet. By coincidence Warsaw completed second so at this point the population of Warsaw can be passed downstream to a subscriber. Unfortunately population of London must wait even more because first we need a population of Paris. Once Paris (immediately followed by Madrid) completes, all remaining results are passed downstream.<\/p>\n<p>Notice how population of London, even though available, must wait dormant until Warsaw and Paris complete. So is\u00a0<code>concatMapEager()<\/code>\u00a0the best possible operator for concurrency? Not quite. Imagine we have a list of thousand cities and for each one we fetch a single 1MB picture. With\u00a0<code>concatMap()<\/code>\u00a0we download pictures sequentially, i.e. slowly. With\u00a0<code>flatMap()<\/code>\u00a0pictures are downloaded concurrently and processed as they arrive, as soon as possible. Now what about\u00a0<code>concatMapEager()<\/code>? In worst case scenario we can end up with\u00a0<code>concatMapEager()<\/code>\u00a0buffering 999 pictures because picture from the very first city happens to be the slowest. Even though we already have 99.9% of the results we cannot process them because we enforce strict ordering.<\/p>\n<h2 id=\"which-operator-to-use\">Which operator to use?<\/h2>\n<p><code>flatMap()<\/code>\u00a0should be your first weapon of choice. It allows efficient concurrency with streaming behavior. But be prepared to receive results out-of-order.\u00a0<code>concatMap()<\/code>\u00a0works well only when provided transformation is so fast the sequential processing is not a problem.\u00a0<code>concatMapEager()<\/code>\u00a0is very convenient, but watch out for memory consumption. Also in worst case scenario you may end up sitting idle, waiting for very few responses.<\/p>\n<h2 id=\"appendix-configuring-retrofit2-client\">Appendix: configuring Retrofit2 client<\/h2>\n<p>The\u00a0<code>GeoNames<\/code>\u00a0service interface that we used throughout this article in fact looks like this:<\/p>\n<pre class=\"brush:java\">public interface GeoNames {\r\n \r\n    @GET(\"\/searchJSON\")\r\n    Single&lt;SearchResult&gt; search(\r\n            @Query(\"q\") String query,\r\n            @Query(\"maxRows\") int maxRows,\r\n            @Query(\"style\") String style,\r\n            @Query(\"username\") String username\r\n    );\r\n \r\n    default Flowable&lt;Long&gt; populationOf(String city) {\r\n        return search(city, 1, \"LONG\", \"s3cret\")\r\n                .map(SearchResult::getGeonames)\r\n                .map(g -&gt; g.get(0))\r\n                .map(Geoname::getPopulation)\r\n                .toFlowable();\r\n    }\r\n \r\n}<\/pre>\n<p>The implementation of non-default method is auto-generated by Retrofit2. Notice that\u00a0<code>populationOf()<\/code>\u00a0returns a one-element\u00a0<code>Flowable&lt;Long&gt;<\/code>\u00a0for simplicity&#8217;s sake. However to fully embrace the nature of this API other implementations would be more reasonable in real world. First of all the\u00a0<code>SearchResult<\/code>\u00a0class returns an ordered list of results (getters\/setters omitted):<\/p>\n<pre class=\"brush:java\">class SearchResult {\r\n    private List&lt;Geoname&gt; geonames = new ArrayList&lt;&gt;();\r\n}\r\n \r\nclass Geoname {\r\n    private double lat;\r\n    private double lng;\r\n    private Integer geonameId;\r\n    private Long population;\r\n    private String countryCode;\r\n    private String name;\r\n}<\/pre>\n<p>After all there are many\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Warsaw,_Indiana\">Warsaws<\/a>\u00a0and\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/London,_Ohio\">Londons<\/a>\u00a0in the world. We silently assume the list will contain at least one element and the\u00a0<a href=\"http:\/\/www.businessinsider.com\/google-just-effectively-killed-the-im-feeling-lucky-button-2010-9\">first one<\/a>\u00a0is the\u00a0<em>right<\/em>\u00a0match. More appropriate implementation should either return all hits or even better\u00a0<code>Maybe&lt;Long&gt;<\/code>\u00a0type to reflect no matches:<\/p>\n<pre class=\"brush:java\">default Maybe&lt;Long&gt; populationOf(String city) {\r\n    return search(city, 1, \"LONG\", \"nurkiewicz\")\r\n            .flattenAsFlowable(SearchResult::getGeonames)\r\n            .map(Geoname::getPopulation)\r\n            .firstElement();\r\n}<\/pre>\n<p>The glue code looks as follows. First Jackson&#8217;s setup in order to parse response from the API:<\/p>\n<pre class=\"brush:java\">import com.fasterxml.jackson.databind.ObjectMapper;\r\n \r\nprivate ObjectMapper objectMapper() {\r\n    return new ObjectMapper()\r\n            .configure(FAIL_ON_UNKNOWN_PROPERTIES, false);\r\n}<\/pre>\n<p><code>FAIL_ON_UNKNOWN_PROPERTIES<\/code>\u00a0is often what you desire. Otherwise you have to map all fields from JSON response and your code will break when API producer introduces new, otherwise backward compatible fields. Then we setup\u00a0<a href=\"http:\/\/square.github.io\/okhttp\/\"><code>OkHttpClient<\/code><\/a>, used underneath by Retrofit:<\/p>\n<pre class=\"brush:java\">import okhttp3.OkHttpClient;\r\nimport okhttp3.logging.HttpLoggingInterceptor;\r\n \r\nprivate OkHttpClient client() {\r\n    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();\r\n    interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);\r\n    return new OkHttpClient.Builder().addInterceptor(interceptor).build();\r\n}<\/pre>\n<p>Sometimes you can skip the configuration of OkHttp client but we added logging interceptor. By default OkHttp logs using\u00a0<code>java.util.logging<\/code>\u00a0so in order to use decent logging framework we must install a bridge at the very beginning:<\/p>\n<pre class=\"brush:java\">import org.slf4j.bridge.SLF4JBridgeHandler;\r\n \r\nstatic {\r\n    SLF4JBridgeHandler.removeHandlersForRootLogger();\r\n    SLF4JBridgeHandler.install();\r\n}<\/pre>\n<p>And finally Retrofit itself:<\/p>\n<pre class=\"brush:java\">import io.reactivex.schedulers.Schedulers;\r\nimport retrofit2.Retrofit;\r\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;\r\nimport retrofit2.converter.jackson.JacksonConverterFactory;\r\n \r\nGeoNames createClient() {\r\n    return new Retrofit.Builder()\r\n            .client(client())\r\n            .baseUrl(\"http:\/\/api.geonames.org\")\r\n            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))\r\n            .addConverterFactory(JacksonConverterFactory.create(objectMapper()))\r\n            .build()\r\n            .create(GeoNames.class);\r\n}<\/pre>\n<p>Calling\u00a0<code>createClient()<\/code>\u00a0will yield a dynamic implementation of\u00a0<code>GeoNames<\/code>\u00a0interface. We used the following dependencies:<\/p>\n<pre class=\"brush:java\">compile 'io.reactivex.rxjava2:rxjava:2.0.6'\r\n \r\ncompile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'\r\ncompile 'com.squareup.retrofit2:converter-jackson:2.0.1'\r\ncompile 'com.squareup.okhttp3:logging-interceptor:3.8.0'\r\n \r\ncompile 'ch.qos.logback:logback-classic:1.1.7'\r\ncompile 'org.slf4j:slf4j-api:1.7.21'\r\ncompile 'org.slf4j:jul-to-slf4j:1.7.21'<\/pre>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"http:\/\/www.nurkiewicz.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager.html\">flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ<\/a> from our <a href=\"http:\/\/www.javacodegeeks.com\/join-us\/jcg\/\">JCG partner<\/a> Tomasz Nurkiewicz at the <a href=\"http:\/\/www.nurkiewicz.com\/\">Java and neighbourhood<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument &#8211; a function from original stream&#8217;s individual item to a (sub-)stream of arbitrary type. In other words if you have a\u00a0Flowable&lt;T&gt;\u00a0you provide a function from\u00a0T\u00a0to\u00a0Flowable&lt;R&gt;\u00a0for arbitrary\u00a0R\u00a0type. After applying any of these operators you end up with\u00a0Flowable&lt;R&gt;. So how are &hellip;<\/p>\n","protected":false},"author":13,"featured_media":112,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[908],"class_list":["post-67846","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-rxjava"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks<\/title>\n<meta name=\"description\" content=\"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks\" \/>\n<meta property=\"og:description\" content=\"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-04T10:00:55+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Tomasz Nurkiewicz\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/tnurkiewicz\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tomasz Nurkiewicz\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\"},\"author\":{\"name\":\"Tomasz Nurkiewicz\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/fb1be85725c10e8361e641fa851e79e1\"},\"headline\":\"flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ\",\"datePublished\":\"2017-08-04T10:00:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\"},\"wordCount\":1225,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"keywords\":[\"RxJava\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\",\"name\":\"flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"datePublished\":\"2017-08-04T10:00:55+00:00\",\"description\":\"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"java-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2017\\\/08\\\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/fb1be85725c10e8361e641fa851e79e1\",\"name\":\"Tomasz Nurkiewicz\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g\",\"caption\":\"Tomasz Nurkiewicz\"},\"description\":\"Java EE developer, Scala enthusiast. Enjoying data analysis and visualization. Strongly believes in the power of testing and automation.\",\"sameAs\":[\"http:\\\/\\\/nurkiewicz.blogspot.com\\\/\",\"https:\\\/\\\/x.com\\\/https:\\\/\\\/twitter.com\\\/tnurkiewicz\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/tomasz-nurkiewicz\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks","description":"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html","og_locale":"en_US","og_type":"article","og_title":"flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks","og_description":"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a","og_url":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2017-08-04T10:00:55+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","type":"image\/jpeg"}],"author":"Tomasz Nurkiewicz","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/tnurkiewicz","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Tomasz Nurkiewicz","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html"},"author":{"name":"Tomasz Nurkiewicz","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/fb1be85725c10e8361e641fa851e79e1"},"headline":"flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ","datePublished":"2017-08-04T10:00:55+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html"},"wordCount":1225,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","keywords":["RxJava"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html","url":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html","name":"flatMap() vs. concatMap() vs. concatMapEager() - RxJava FAQ - Java Code Geeks","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","datePublished":"2017-08-04T10:00:55+00:00","description":"There are three, seamlessly similar operators in RxJava 2.x:\u00a0flatMap(),\u00a0concatMap()\u00a0and\u00a0concatMapEager(). All of them accept the same argument - a","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","width":150,"height":150,"caption":"java-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2017\/08\/flatmap-vs-concatmap-vs-concatmapeager-rxjava-faq.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"flatMap() vs. concatMap() vs. concatMapEager() &#8211; RxJava FAQ"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/fb1be85725c10e8361e641fa851e79e1","name":"Tomasz Nurkiewicz","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f2a8f9f1060fc7c1161c42f8ba901e0e79fb767dec39ec34b2d3b95cab9dc728?s=96&d=mm&r=g","caption":"Tomasz Nurkiewicz"},"description":"Java EE developer, Scala enthusiast. Enjoying data analysis and visualization. Strongly believes in the power of testing and automation.","sameAs":["http:\/\/nurkiewicz.blogspot.com\/","https:\/\/x.com\/https:\/\/twitter.com\/tnurkiewicz"],"url":"https:\/\/www.javacodegeeks.com\/author\/tomasz-nurkiewicz"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/67846","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/13"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=67846"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/67846\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/112"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=67846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=67846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=67846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}