В чем разница между методами `map()` и `flatMap()` в Java Stream API?

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

Ответ

Оба метода являются промежуточными операциями Stream API, но служат для разных сценариев преобразования данных.

Аспект map() flatMap()
Назначение Преобразует каждый элемент потока в один новый элемент. Преобразует каждый элемент потока в поток элементов, а затем объединяет все потоки в один.
Сигнатура <R> Stream<R> map(Function<T, R> mapper) <R> Stream<R> flatMap(Function<T, Stream<R>> mapper)
Соотношение 1 → 1 (один вход → один выход) 1 → N → 1 (один вход → поток выходов → объединенный поток)

Пример map(): Преобразование списка строк в список их длин.

List<String> names = List.of("Anna", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
        .map(String::length) // Function<String, Integer>
        .toList(); // Результат: [4, 3, 7]

Пример flatMap(): "Разворачивание" списка списков в единый список.

List<List<String>> listOfLists = List.of(
        List.of("a", "b"),
        List.of("c", "d", "e")
);
List<String> flatList = listOfLists.stream()
        .flatMap(List::stream) // Function<List<String>, Stream<String>>
        .toList(); // Результат: ["a", "b", "c", "d", "e"]

Практическое применение flatMap():

  • Обработка Optional: Stream<Optional<User>>Stream<User>.
  • Разделение строк: stream.map(line -> line.split("\s+")) вернет Stream<String[]>, а flatMap(Arrays::stream)Stream<String>.