skip to content

What happens to the inner streams that flatMap produces, and why does that matter for resources like Files.lines?

level: seniorimportance: should knowfreq 45%

answer

  1. flatMap closes each inner stream after consuming it
  2. Javadoc: mapped stream closed after contents placed into output
  3. Files.lines inner streams -> closed by flatMap, no FD leak
  4. Outer/top-level stream still needs try-with-resources
  5. Don't close inner streams yourself in the lambda

basics

~20 s

flatMap consumes each inner stream it creates and closes it when done. That matters because some streams hold resources — like Files.lines opening a file — so flatMap closing them after consumption helps avoid leaking file handles.

solid answer

~50 s

flatMap calls your mapping function lazily for each element, gets back an inner stream, pulls its elements into the outer stream, and then closes that inner stream before moving on. The Javadoc explicitly states each mapped stream is closed after its contents are placed into the output. This is important for streams backed by I/O resources: Files.lines(path) returns a Stream<String> tied to an open file, and if you produce one per element inside flatMap, flatMap closes each as it finishes, preventing file-descriptor leaks. By contrast, a top-level resource stream still needs try-with-resources because nobody else closes it. A subtle consequence: because flatMap fully consumes each inner stream and closes it, short-circuiting (findFirst, limit) within a single inner stream works, but historically flatMap did not always short-circuit across the boundary cleanly — improved in later JDKs. Net: rely on flatMap to close inner streams, but still close the outer one yourself.

go deeper

for a junior

Aware that some streams need closing but may not know flatMap handles inner ones.

for a middle

States that flatMap consumes inner streams and that resource streams need closing, with some hesitation on who closes what.

for a senior

Cites the close-after-consume guarantee, applies it to Files.lines, and still wraps the top-level resource stream in try-with-resources.

for a principal

Discusses short-circuiting/laziness nuances across the flatMap boundary, JDK version history, and parallel-stream splitting costs of flattening.

## Recap: flatMap mechanics `flatMap(Function<T,Stream<R>> f)` calls `f` on each element to obtain an **inner stream**, drains that inner stream's elements into the **outer** (resulting) stream, then repeats for the next element. Crucially, it does this **lazily**: an inner stream is only created when the pipeline pulls the corresponding element. ## The lifecycle guarantee: flatMap closes inner streams The `Stream.flatMap` Javadoc states: *each mapped stream is closed after its contents have been placed into this stream.* So `flatMap` takes ownership of the inner streams it receives from your function and **closes** them when it is finished consuming each one. You do **not** (and should not) close them yourself inside the lambda. Why does 'closing a stream' even matter? Most streams (from collections) hold nothing special and closing is a no-op. But a stream can be **resource-backed** — created over a file, a directory, or a network handle. Such streams implement `AutoCloseable` and free the underlying resource (e.g. a file descriptor) on `close()`. ## Worked example: Files.lines inside flatMap `Files.lines(Path)` returns a `Stream<String>` that **keeps the file open** while you read it; you must close it to release the OS file handle. Consider reading every line of many files: ```java List<Path> paths = ...; try (Stream<Path> pathStream = paths.stream()) { List<String> allLines = pathStream .flatMap(p -> { try { return Files.lines(p); } // one resource-backed inner stream per file catch (IOException e) { throw new UncheckedIOException(e); } }) .collect(Collectors.toList()); // flatMap CLOSES each Files.lines stream after consuming it -> no FD leak } ``` Here each `Files.lines(p)` opens a file. Because `flatMap` closes every inner stream after draining it, the file handles are released as the pipeline progresses — no descriptor leak even across thousands of files. If you instead collected the line-streams into a list and read them later, nothing would close them and you'd leak. ## What flatMap does NOT close `flatMap` only closes the **inner** streams it creates from your function. The **outer/top-level** stream is your responsibility. If that outer stream is itself resource-backed (e.g. you started from `Files.lines(...)` directly), wrap it in **try-with-resources**, because nothing upstream closes it for you. ## Laziness, ordering, and short-circuiting - **Laziness:** inner streams are materialized on demand, so a `limit(5)` downstream can avoid opening files you never reach (subject to JDK version behavior). - **Short-circuiting:** within a single inner stream, operations like `findFirst`/`anyMatch` can stop early. Across the flatMap boundary, early JDKs eagerly consumed an entire inner stream; this was improved (the flatMap short-circuiting fix in later 8.x/9+ releases), but the safe mental model for interviews is: flatMap consumes-then-closes each inner stream. - **Parallel streams:** flatMap can hurt parallel performance because the flattening step is harder to split; mention this as a tradeoff. ## Practical rules 1. Trust `flatMap` to close inner streams — don't manually close them in the lambda. 2. Still use try-with-resources for the **top-level** resource stream. 3. Be aware resource-backed inner streams (Files.lines, Files.list, Files.walk) are the case where this guarantee earns its keep.

  • Does flatMap close the top-level stream you started the pipeline with?
    No. flatMap only closes the inner streams it creates from your mapping function. A resource-backed top-level stream must be closed by you, typically via try-with-resources.
  • Why is the inner-stream close guarantee especially relevant for Files.lines?
    Files.lines holds an open file descriptor until closed; producing one per element inside flatMap could leak handles, but flatMap closing each after consumption prevents that leak.

saying these in an interview costs you the question

  • Claiming you must manually close the inner Files.lines stream inside flatMap
  • Thinking flatMap closes the outer stream too
  • Assuming closing a stream is always required even for collection streams
  • Believing flatMap leaks file handles by design

context