In Project Reactor, what is the difference between map and flatMap on a Flux or Mono?
answer
- map = 1:1 sync, returns value
- flatMap = 1:N async, returns Publisher
- flatMap subscribes + merges inner streams
- map would give Flux<Mono<T>>
- flatMap interleaves, no order guarantee
basics
~10 smap transforms each item synchronously, one value in -> one value out. flatMap transforms each item into a Publisher (another Flux/Mono, often async) and flattens all those inner streams into one output stream.
solid answer
~40 smap is a 1-to-1 synchronous transformation: you return a plain value and the operator wraps it back into the stream. flatMap is a 1-to-N asynchronous transformation: your function returns a Publisher (a Mono or Flux), typically an async call like a WebClient request or an R2DBC query, and flatMap subscribes to each of those inner publishers and merges their emissions into a single output Flux. Use map when the work is a pure, in-memory computation; use flatMap when each element triggers another reactive/async operation so you avoid ending up with a Flux<Mono<T>>. A key gotcha: flatMap subscribes to inner publishers eagerly and interleaves results, so it does NOT preserve source order by default.
code
java · 11 lines// map: synchronous 1-to-1
Flux<Integer> doubled = Flux.just(1, 2, 3)
.map(n -> n * 2); // 2, 4, 6
// flatMap: asynchronous 1-to-N, flattens inner Publishers
Flux<User> users = Flux.just("u1", "u2", "u3")
.flatMap(id -> userRepository.findById(id)); // Mono<User> per id
// WRONG: map with an async call yields a nested stream of publishers
Flux<Mono<User>> nested = Flux.just("u1")
.map(id -> userRepository.findById(id)); // almost never usefulgo deeper
Must know map = sync 1:1, flatMap = async returning a Publisher. Recognizing that flatMap is for I/O calls is the core takeaway.
Should add that flatMap subscribes eagerly, interleaves, and has a concurrency parameter; and that map for an async call yields a useless nested Flux<Mono<T>>.
Frames the choice around ordering and back-pressure, contrasts with concatMap/flatMapSequential, and knows the concurrency default (256).
Discusses subscription semantics, scheduler/blocking implications, and prefetch/concurrency tuning for throughput vs. resource limits.
## Setup **Project Reactor** is the reactive-streams library underneath **Spring WebFlux**. Its two publisher types are **Mono<T>** (0 or 1 item) and **Flux<T>** (0..N items). Operators are chained transformations you build on top of these publishers. ## map `map(Function<T,R>)` is a **synchronous, one-to-one** transformation. For every element `T` the source emits, your function returns a plain value `R`, and `map` re-wraps it into the stream. Nothing async happens; there is no new Publisher. ```java Flux.just(1, 2, 3).map(n -> n * 2); // -> 2, 4, 6 ``` Think of it as `List.stream().map(...)` but on a reactive stream. ## flatMap `flatMap(Function<T, Publisher<R>>)` is an **asynchronous, one-to-many** transformation. Your function returns a **Publisher** (another `Mono` or `Flux`) for each element. `flatMap` then **subscribes to each inner publisher** and **merges** their emissions into one flat output stream — hence the name (it flattens `Flux<Publisher<R>>` into `Flux<R>`). ```java Flux.just("a", "b") .flatMap(id -> webClient.get().uri("/x/{id}", id).retrieve().bodyToMono(String.class)); ``` Use it whenever each element needs another **reactive/async call**: a `WebClient` request, an R2DBC repository lookup, etc. If you used `map` for that, you'd get a `Flux<Mono<R>>` — a stream of un-subscribed publishers — which is almost never what you want. ## Why the distinction matters - **Concurrency:** `flatMap` subscribes to inner publishers **eagerly**, up to a concurrency limit (default 256, overridable via `flatMap(fn, concurrency)`). Multiple inner calls run in flight at once. - **Ordering:** because inner streams complete at different times, `flatMap` **interleaves** results — output order does NOT match source order. If you need order, use `concatMap` (sequential) or `flatMapSequential` (concurrent but reordered). - **map never reorders** because it's synchronous 1-to-1. ## Gotchas - Returning a value from `flatMap` where you meant `map` (or vice versa) is a classic compile error/confusion: `flatMap` expects a Publisher, `map` expects a plain value. - Blocking inside `map` (e.g. a JDBC call) stalls the pipeline — that work belongs in `flatMap` on a proper scheduler, or better, a reactive driver. - `Mono.flatMap` returns a Mono (0..1); `Mono.flatMapMany` is used when the inner publisher is a Flux and you want many results out of one input. ## When to use - **map** — pure transformation, formatting, field extraction, DTO mapping (no I/O). - **flatMap** — per-element async I/O where order doesn't matter and throughput does.
- If you use flatMap over a WebClient call, is the output in the same order as the input?No. flatMap subscribes to inner publishers eagerly and merges as they complete, so faster responses emit first. Use concatMap for strict order or flatMapSequential to keep order while still running concurrently.
- What's the difference between Mono.flatMap and Mono.flatMapMany?Mono.flatMap maps the single value to another Mono (0..1 out). Mono.flatMapMany maps it to a Publisher that can emit many items, returning a Flux — useful when one input fans out to many results.
saying these in an interview costs you the question
- Thinking map can do async I/O
- Believing flatMap preserves source order
- Not knowing flatMap subscribes to inner publishers
- Confusing which operator returns a Publisher vs a value