Question 4 · Section 8

What does map() operation do?

Takes a Function — a functional interface with the method R apply(T t).

Language versions: English Russian Ukrainian

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 = 1 million objects on the heap (~24MB), IntStream = one int[] array (~4MB). 6x memory difference.

Map vs FlatMap

  • map: returns one object for each input
  • flatMap: 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

  1. You don’t need the transformation result — use forEach (for actions) or peek (for debugging)
  2. Filtering + transformation in one — sometimes one loop with if + transform is better
  3. 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 map contains System.out.println or log.info — this is a sign of bad design. For logging, use peek().

Interview Cheat Sheet

Must know:

  • map(Function) — intermediate operation, one-to-one transformation
  • Takes Function<T, R>, returns Stream<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]]