С каким функциональным интерфейсом взаимодействует метод flatMap в Java Stream API?

Ответ

Метод flatMap в Stream<T> ожидает аргумент типа Function<? super T, ? extends Stream<? extends R>>.

Разбор интерфейса:

  • T — тип элемента исходного потока.
  • R — тип элемента в результирующем потоке.
  • Функция принимает элемент T и возвращает новый поток (Stream<R>) элементов R.

Почему именно Function? Потому что операция является преобразованием (маппингом) каждого элемента, но с условием, что результат преобразования — это не один элемент, а множество (поток).

Пример: Разделение строк на символы

List<String> words = List.of("Hello", "World");
List<String> letters = words.stream()
    // Function<String, Stream<String>>: строка -> поток символов
    .flatMap(word -> word.chars()
                        .mapToObj(c -> String.valueOf((char) c)))
    .toList(); // Результат: [H, e, l, l, o, W, o, r, l, d]

Сравнение с map:

  • map(Function<T, R>): T -> R (один к одному).
  • flatMap(Function<T, Stream<R>>): T -> Stream<R> (один ко многим, с последующим слиянием).

Ответ 18+ 🔞

А, ну это же классика, блядь! Смотри, сейчас разжуём, как бабушка котлету.

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

Смотри на его рожу: Function<? super T, ? extends Stream<? extends R>>. Выглядит страшно, как пизда с ушами, но на деле всё просто.

  • T — это что у тебя было в начале. Строка, число, объект — что угодно.
  • R — это что должно получиться в конце. Совсем другое, или то же самое — неважно.
  • А сама функция — это такой волшебный ящик. Ты суёшь в него один T, а он тебе выплёвывает целый новый поток (Stream<R>) этих самых R.

Почему Function, а не что-то другое? Да потому что это и есть преобразование, ёпта! Маппинг, блядь. Но не простой, а плоский. Ты маппишь каждый элемент на поток, а потом все эти потоки херак — и сливаются в один плоский, без этих вложенных структур, которые мозг выебать могут.

Пример, чтобы не быть мудаком: Допустим, у тебя список слов, а тебе нужны все буквы по отдельности.

List<String> words = List.of("Hello", "World");
List<String> letters = words.stream()
    // Вот тут магия: берём слово (String), а на выходе — поток букв (Stream<String>)
    .flatMap(word -> word.chars()
                        .mapToObj(c -> String.valueOf((char) c)))
    .toList(); // И получаем: [H, e, l, l, o, W, o, r, l, d]

Видишь? Из слова "Hello" получился поток "H", "e", "l", "l", "o". Из "World" — свой поток. А flatMap взял и склеил их в один общий список букв. Красота, блядь!

Чем отличается от простого map? Да всё просто, как три копейки:

  • map: Один T зашёл — один R вышел. Как на конвейере: кирпич -> окрашенный кирпич.
  • flatMap: Один T зашёл — много R вылезло. Как хрен знает что: кирпич -> целая куча стекляшек, если его разбить. А потом все стекляшки от всех кирпичей сгребаются в одну кучу.

Вот и вся философия, блядь. Не усложняй, а то волнение ебать почувствуешь.