map()
Transform each element into exactly one new value, such as an entity into a response DTO, and use the primitive variants to avoid boxing.
<R> Stream<R> map(Function<? super T, ? extends R> mapper)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$mapper | Function<? super T, ? extends R> | A non-interfering, stateless function applied to each element. Its return value becomes the element of the new stream; returning null puts a null in the stream. |
Return value
Stream<R>A new stream of the results of applying the mapper to each element, one output per input, in the same encounter order.
Errors
Nothing directly. Exceptions thrown by the mapper surface when the terminal operation runs. Passing a null mapper throws NullPointerException.
map applies a function to every element and returns a stream of the results. It is strictly one in, one out: the new stream has the same number of elements, in the same order, and the element type can change from T to R.
The everyday back-end use
Most map calls in a service turn one shape into another: an entity into a response DTO, a row into a domain record, a string into a parsed number. Doing this in a mapping step is how you keep fields such as password hashes and internal flags out of JSON responses:
List<UserResponse> body = users.stream()
.map(u -> new UserResponse(u.id(), u.email()))
.toList();Method references keep simple mappings short: map(String::trim), map(Integer::parseInt), map(UserResponse::from). A static factory such as from on the DTO is a common way to keep the mapping in one place.
Primitive variants
map always produces objects, so mapping to numbers boxes every value into an Integer or Long. mapToInt, mapToLong and mapToDouble return IntStream, LongStream and DoubleStream, which offer sum(), average() and summaryStatistics() and avoid the boxing. Go back with boxed() or mapToObj.
What map is not for
- Zero or many results per element. If the function returns a list or a stream,
mapgives you a stream of lists. UseflatMap, ormapMulti(Java 16) to push zero or more values imperatively. - Side effects. Calling a remote API or saving to the database inside
maphides I/O in what reads like a pure transformation, and laziness means it may not run for every element. Use a loop for work whose effects matter. - Changing the element in place.
map(u -> { u.setActive(true); return u; })mutates objects that other code may still hold. Prefer creating a new value, which records make easy.
Nulls and checked exceptions
If the mapper returns null, the stream carries a null and a later step may throw NullPointerException; map(cache::get) is a common source of this. Filter them out, or map to Optional and flatten. A Function cannot throw checked exceptions, so a mapper that parses JSON or reads a file has to catch and wrap the exception, typically in an UncheckedIOException or your own runtime exception.
Example
When to use it
- Turn JPA entities or JDBC rows into response records so internal fields never reach the JSON body.
- Parse a comma-separated query parameter into a list of numeric IDs before querying the database.
- Sum line totals in pence with mapToLong when calculating an order total for an invoice.
Frequently asked questions
What is the difference between map and flatMap in Java streams?
map turns each element into exactly one result, so a function that returns a List gives you a Stream<List<X>>. flatMap expects the function to return a Stream and joins all of those streams into one flat Stream<X>.
How do I convert a list of objects into a list of one of their fields?
Map with a getter or record accessor and collect: users.stream().map(User::email).toList(). For numeric fields you want to total, use mapToInt or mapToLong and call sum().
Can I throw a checked exception inside Stream.map?
Not directly: java.util.function.Function does not declare checked exceptions, so the code will not compile. Catch the exception in the lambda and rethrow it wrapped in an unchecked exception such as UncheckedIOException, or use a plain loop if the error handling is central to the code.
More examples
mapToInt for totals without boxing
mapToInt returns an IntStream, which has sum() and summaryStatistics() built in. Keeping money as whole pence in an int or long avoids floating-point rounding in totals.
record Line(String sku, int qty, int unitPence) {}
void main() {
List<Line> cart = List.of(
new Line("A", 2, 450),
new Line("B", 1, 1299),
new Line("C", 3, 100));
int totalPence = cart.stream()
.mapToInt(l -> l.qty() * l.unitPence())
.sum();
IO.println(totalPence);
IntSummaryStatistics qty = cart.stream()
.mapToInt(Line::qty)
.summaryStatistics();
IO.println(qty.getMin() + " " + qty.getMax() + " " + qty.getAverage());
}
// Output:
// 2499
// 1 3 2.0Parsing input, and where nulls come from
Chained maps read as a series of small conversions. A lookup that misses returns null, and map passes that null straight through, so filter it out before anything calls a method on it.
void main() {
String query = "ids=4, 8 ,15";
List<Integer> ids = Arrays.stream(query.substring(4).split(","))
.map(String::trim)
.map(Integer::parseInt)
.toList();
IO.println(ids);
Map<String, String> names = Map.of("u1", "Ada", "u2", "Linus");
List<String> resolved = Stream.of("u1", "u9", "u2")
.map(names::get)
.toList();
IO.println(resolved);
}
// Output:
// [4, 8, 15]
// [Ada, null, Linus]mapMulti when one element gives zero or more results
map must produce exactly one value per element, so it cannot skip or duplicate. mapMulti (Java 16) hands you a sink to push zero or more values into, here doing a type check and cast in one step.
void main() {
List<Object> raw = List.of(1, "two", 3, 4.5, 5);
List<Integer> ints = raw.stream()
.<Integer>mapMulti((o, sink) -> {
if (o instanceof Integer i) {
sink.accept(i);
}
})
.toList();
IO.println(ints);
}
// Output:
// [1, 3, 5]
Discussion