С какими типами данных работает метод flatMap в Java Stream API?

Ответ

Метод flatMap работает с элементами потока (Stream<T>), которые сами являются контейнерами или могут быть преобразованы в потоки. Его задача — "развернуть" вложенную структуру.

Основной сценарий: Преобразование Stream<Collection<T>> или Stream<Stream<R>> в Stream<R>.

Пример: Разворот списка списков

List<List<String>> nestedList = List.of(
    List.of("a", "b"),
    List.of("c", "d")
);

List<String> flatList = nestedList.stream()
    .flatMap(Collection::stream) // Преобразуем каждый List в Stream
    .toList(); // Результат: ["a", "b", "c", "d"]

Другие применения:

  • Optional<T>: Optional::stream или optional.flatMap(...) для цепочки вычислений, возвращающих Optional.
  • CompletableFuture<T>: Для асинхронных цепочек, где каждая операция возвращает новый Future.

Ключевое отличие от map: map преобразует T -> R, а flatMap преобразует T -> Stream<R>, а затем объединяет (flattens) все полученные потоки в один.

Ответ 18+ 🔞

А, ну этот ваш flatMap... Это ж как тот чувак на стройке, который вместо того, чтобы аккуратно кирпичи складывать, взял и все паллеты разом опрокинул — один большой кучей получилось, зато быстро, блядь!

Смотри, в чём суть. Есть у тебя, допустим, не просто список, а список списков. Как мешок, в котором другие мешки лежат. И ты хочешь не мешки, а всё, что внутри них, в одну кучу. Вот flatMap — это как взять каждый внутренний мешок, вытряхнуть из него всё в общую кучу, а сам мешок выкинуть нахуй. И получается один плоский список, без всякой этой вложенной хуйни.

Вот, смотри на код, тут всё понятно:

List<List<String>> nestedList = List.of(
    List.of("a", "b"),
    List.of("c", "d")
);

List<String> flatList = nestedList.stream()
    .flatMap(Collection::stream) // Берём каждый внутренний List и делаем из него Stream
    .toList(); // И всё собираем! Результат: ["a", "b", "c", "d"]

Видишь? Было [[a, b], [c, d]]. flatMap пришёл, каждый внутренний лист превратил в стрим ([a, b] -> стрим, [c, d] -> стрим), а потом все эти стримы склеил в один: [a, b, c, d]. Элегантно, ёпта!

А теперь главное, чтобы не спутать с обычным map. Это две большие разницы, как говорят в Одессе.

  • map — это как взять каждый кирпич и покрасить его в другой цвет. На входе кирпич, на выходе — крашеный кирпич. Один к одному.
  • flatMap — это как взять каждый ящик с кирпичами, открыть его, и вывалить все кирпичи на общую груду. На входе ящик, а на выходе — не ящик, а поток кирпичей из него. А потом все такие потоки смешиваются.

Его, этого зверя, не только со списками используют. О, нет! Он вездесущий, как таракан.

  • С Optional — идеально, когда у тебя одна операция возвращает Optional, а тебе нужно сделать другую операцию, которая тоже возвращает Optional. Чтоб не получалось Optional<Optional<...>> — этой хуйни, которая мозг выносит. flatMap сам всё развернёт.
  • С CompletableFuture — для асинхронщины, когда одна асинхронная операция цепляется за другую. Чтобы не было Future<Future<...>>, а был один ровненький Future.

Короче, flatMap — это твой инструмент, когда нужно преобразовать, а потом выпрямить. Превратить T в Stream<R>, а потом все эти ручейки сливаются в одну полноводную реку. Пиздец как удобно, когда понимаешь, зачем он нужен!