Definition
- **ofNullable(value)** – Java 9; returns `Stream.of(value)` if non-null, `Stream.empty()` if null. Eliminates null-check ternaries. - **Best use** – inside `flatMap` to safely include nullable fields: `flatMap(t -> Stream.ofNullable(t.getNote()))`. - **concat(a, b)** – lazily concatenates two streams; elements from `a` first, then `b`. Neither consumed until terminal op. - **Close propagation** – closing the concatenated stream closes both inputs (important for Files.lines). - **3+ streams** – prefer `Stream.of(s1, s2, s3).flatMap(s -> s)` over nested concat to avoid deep pipeline nesting.
ofNullable is like checking a bank mailbox: if there is a letter (non-null), put it on the conveyor belt. If empty (null), put nothing. No NullPointerException. concat is like connecting two conveyor belts end-to-end: the first belt's items come through, then the second belt's items.
Understanding the Concepts
What is Stream.ofNullable?
Stream.ofNullable(value) (Java 9) returns a one-element stream if value is non-null, or an empty stream if null.
package com.bank.service;
import java.util.stream.Stream;
public class AccountService {
// BEFORE Java 9: verbose null check
public Stream<String> getSymbolStream(String symbol) {
return symbol != null ? Stream.of(symbol) : Stream.empty();
}
// AFTER Java 9: one-liner
public Stream<String> getSymbolStreamClean(String symbol) {
return Stream.ofNullable(symbol);
}
}
How is ofNullable used in flatMap?
import java.util.List;
import java.util.stream.Stream;
// Collect all non-null risk notes from trades
List<String> notes = trades.stream()
.flatMap(t -> Stream.ofNullable(t.getRiskNote()))
.toList();
// Null notes become empty streams → skipped. No NullPointerException.
// For nullable collections:
Stream<String> txns = Stream.ofNullable(nullableTransactionList)
.flatMap(java.util.Collection::stream);
// null list → empty stream. Non-null list → stream of elements.
What is Stream.concat?
import java.util.stream.Stream;
// Merge two trade streams from different sources
Stream<Trade> combined = Stream.concat(
exchangeTrades.stream(),
internalTrades.stream());
// Lazy: no processing yet. Elements from exchange first, then internal.
List<Trade> all = combined.filter(Trade::isSettled).toList();
// NOW both streams are consumed through the filter.
How do you merge 3+ streams?
// WRONG: deeply nested concat
Stream<Trade> nested = Stream.concat(
Stream.concat(exchangeTrades.stream(), internalTrades.stream()),
manualTrades.stream());
// Deep nesting, hard to read for many sources
// CORRECT: flatMap identity for many streams
Stream<Trade> flat = Stream.of(
exchangeTrades.stream(),
internalTrades.stream(),
manualTrades.stream()
).flatMap(s -> s);
// Clean for any number of sources
What about resource-backed streams?
import java.nio.file.Files;
import java.nio.file.Path;
// concat propagates close() to both streams
try (Stream<String> combined = Stream.concat(
Files.lines(Path.of("accounts_q1.csv")),
Files.lines(Path.of("accounts_q2.csv")))) {
long total = combined.filter(line -> line.contains("SAVINGS")).count();
}
// Both files are closed when the combined stream is closed
What should a senior engineer know?
- ofNullable is preferred over filter(Objects::nonNull) when the nullable value comes from a method call rather than a stream element.
- concat preserves encounter order: all elements from the first stream, then all from the second.
- Both are factory methods on the Stream interface: no utility class needed.
What are common interview follow-up questions?
- Can Stream.of(null) work? (No, it throws NullPointerException. Use ofNullable for nullable values.)
- Is concat eager or lazy? (Lazy. Neither stream is consumed until a terminal operation triggers.)
- Does concat handle parallel streams? (Yes. The resulting stream can be parallelized.)
Visual Overview
Stream.ofNullable:
┌───────────────┐ ┌──────────────┐
│ value = null │ ──▶ │ Stream.empty()│
└───────────────┘ └──────────────┘
┌───────────────┐ ┌─────────────────┐
│ value = "SAV" │ ──▶ │ Stream.of("SAV")│
└───────────────┘ └─────────────────┘
Stream.concat(a, b):
Stream A: [1, 2, 3]
Stream B: [4, 5, 6]
concat: [1, 2, 3, 4, 5, 6] (lazy)
Key Takeaway
Stream.ofNullable (Java 9) returns a one-element stream if the value is non-null, or an empty stream if null. It replaces the verbose value != null ? Stream.of(value) : Stream.empty() ternary. The most common use is inside flatMap to safely include nullable values: trades.stream().flatMap(t -> Stream.ofNullable(t.getNote())) collects only non-null notes without NullPointerException.
Stream.concat lazily merges two streams end-to-end: elements from the first stream, then the second. Neither is consumed until a terminal operation triggers. For merging more than two streams, Stream.of(s1, s2, s3).flatMap(s -> s) is cleaner than nested concat calls.
In a banking consolidated statement service, three data sources may be null. Stream.ofNullable(cards).flatMap(Collection::stream) safely converts null to empty. Then Stream.of(s1, s2, s3).flatMap(s -> s) merges all sources into one stream.
Remember one sentence: ofNullable safely converts a nullable value to a zero-or-one element stream, and concat lazily merges two streams end-to-end.
---