Как работает метод flatMap() в Java Stream API?

«Как работает метод flatMap() в Java Stream API?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

flatMap() преобразует каждый элемент потока в новый поток, затем объединяет все потоки в один. Это позволяет:

  • "Разгладить" вложенные коллекции
  • Преобразовать один элемент в несколько
  • Обработать Optional значения

Базовый пример:

List<List<String>> nestedLists = Arrays.asList(
    Arrays.asList("a", "b", "c"),
    Arrays.asList("d", "e"),
    Arrays.asList("f", "g", "h")
);

List<String> flatList = nestedLists.stream()
    .flatMap(List::stream)      // Преобразуем List<String> в Stream<String>
    .collect(Collectors.toList());
// Результат: [a, b, c, d, e, f, g, h]

Преобразование 1:N (один ко многим):

List<String> words = Arrays.asList("Hello", "World");

List<String> letters = words.stream()
    .flatMap(word -> word.chars()
        .mapToObj(c -> String.valueOf((char) c)))
    .collect(Collectors.toList());
// Результат: [H, e, l, l, o, W, o, r, l, d]

Работа с Optional:

List<Optional<String>> optionals = Arrays.asList(
    Optional.of("value1"),
    Optional.empty(),
    Optional.of("value2")
);

List<String> values = optionals.stream()
    .flatMap(Optional::stream)  // Java 9+
    .collect(Collectors.toList());
// Результат: [value1, value2]

Важные особенности:

  1. Null-безопасность: Если элемент null, будет NullPointerException
  2. Порядок: Сохраняет порядок элементов из исходных потоков
  3. Ленивость: Как и другие промежуточные операции, выполняется лениво