What does map() operation do?
Takes a Function
Junior Level
map(Function) is an intermediate operation that transforms each element of the stream into another element (“one-to-one”).
Takes a Function<T, R> — a functional interface with the method R apply(T t).
List<String> words = List.of("hello", "world");
// Transform words to their length
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// Result: [5, 5]
// Transform to uppercase
List<String> upper = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// Result: ["HELLO", "WORLD"]
Important: map always returns one object for each input element.
Middle Level
Internal implementation
map integrates into the Sink chain:
public void accept(T t) {
downstream.accept(mapper.apply(t));
}
Due to laziness, if the result of map is not used by a terminal operation, the transform function will never be called.
Avoid Wrapper Hell (Autoboxing)
// BAD — creates millions of Integer objects on the heap
list.stream().map(s -> s.length()).reduce(0, Integer::sum);
// GOOD — works with primitives on the stack
list.stream().mapToInt(String::length).sum();
Primitive Streams (IntStream, LongStream, DoubleStream) dramatically reduce GC load and memory consumption.
IntStream/LongStream/DoubleStream store primitives directly, without wrappers (Integer, Long). For 1 million elements: Stream
Map vs FlatMap
map: returns one object for each inputflatMap: returns a stream of objects (from 0 to infinity), which are “flattened” into a single stream
Function Purity
The function in map should be pure (no side effects). In practice impure functions work, but with risks: in parallelStream the result will be unpredictable.
- Do not modify the input object (immutability)
- No side effects (do not write to DB, do not modify static fields)
- Return the same result for the same input data
When NOT to use map
- You don’t need the transformation result — use forEach (for actions) or peek (for debugging)
- Filtering + transformation in one — sometimes one loop with if + transform is better
- Working with side effects — map is not for I/O, use forEach
Senior Level
Method References and JIT optimization
Use String::toUpperCase instead of s -> s.toUpperCase(). This is not only cleaner but also helps the JIT compiler better optimize calls through invokedynamic.
Null Handling
If the mapping function returns null, the stream will continue with a null element. This often leads to NPE in subsequent links:
// Safe approach
stream.map(User::getEmail)
.filter(Objects::nonNull)
.map(String::toLowerCase)
Checked Exceptions
Lambdas in map cannot throw checked exceptions. You have to wrap them in RuntimeException or use helper interfaces:
// Wrapping checked exception
stream.map(s -> {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
})
Object Allocation and Highload
If the function in map creates heavy objects, consider reusing objects (Object Pooling), although this is difficult to implement in streams.
Diagnostics
- Type Changes: Watch how the stream type changes along the chain — IntelliJ IDEA highlights types
- Side Effect Detection: If
mapcontainsSystem.out.printlnorlog.info— this is a sign of bad design. For logging, usepeek().
Interview Cheat Sheet
Must know:
map(Function)— intermediate operation, one-to-one transformation- Takes
Function<T, R>, returnsStream<R> - Primitive Streams (mapToInt, mapToLong) avoid autoboxing and save memory
- Mapping function should be pure: no side effects, deterministic
- Method References (
String::toUpperCase) are preferred over lambdas — cleaner and help JIT - If mapper returns null — stream continues with a null element (NPE risk)
Common follow-up questions:
- map vs flatMap? — map returns one object, flatMap returns a stream of objects that is “flattened”
- Why is mapToInt better than map? — IntStream stores primitives in an array, Stream
creates objects on the heap - How to handle checked exceptions in a lambda? — Wrap in RuntimeException or use helper interfaces
- Can you use map for I/O? — No, map is not for side effects, use forEach
Red flags (DO NOT say):
- “map can return multiple objects for one element” — that is flatMap
- “Autoboxing does not affect performance” — for millions of elements the memory difference is 6x
- “Function in map can modify external variables” — this violates purity and breaks parallelStream
- “map and forEach are interchangeable” — map transforms, forEach performs an action
Related topics:
- [[7. What does flatMap() operation do]]
- [[8. What is the difference between map() and flatMap()]]
- [[3. What does filter() operation do]]
- [[5. What does collect() operation do]]