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

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

Ответ

Оба метода используются для работы с вложенными структурами в потоках, но имеют разные сигнатуры и применения.

flat() — это метод интерфейса Stream, который существует только для специализированных потоков примитивов (IntStream, LongStream, DoubleStream). Он преобразует поток потоков примитивов в один "плоский" поток.

// flat() для IntStream
Stream<IntStream> streamOfIntStreams = Stream.of(IntStream.of(1,2), IntStream.of(3,4));
IntStream flatIntStream = streamOfIntStreams.flatMapToInt(s -> s); // Используется flatMapToInt
// ИЛИ, если у нас уже есть IntStream
IntStream.of(1,2,3).flatMap(...) // flatMap для примитивов

flatMap() — это основной и чаще используемый метод в Stream<T>. Он выполняет две операции:

  1. Map (отображение): Применяет функцию к каждому элементу, которая возвращает новый поток (Stream<R>).
  2. Flatten ("выравнивание"): Объединяет все полученные потоки в один результирующий поток.
// flatMap() для Stream<String>
List<List<String>> listOfLists = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d")
);

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

// Другой пример: разбить строки на слова
List<String> sentences = Arrays.asList("Hello world", "Java Streams");
List<String> words = sentences.stream()
        .flatMap(sentence -> Arrays.stream(sentence.split(" "))) // String -> Stream<String>
        .collect(Collectors.toList()); // ["Hello", "world", "Java", "Streams"]

Сравнение

  • Назначение: flat() — для "выравнивания" потоков примитивов. flatMap() — для преобразования элемента в поток и последующего объединения всех потоков.
  • Использование: flatMap() универсален и применяется в 99% случаев для работы с Stream<T>. flat() — узкоспециализированный метод.
  • Ключевая идея flatMap: Решает проблему map(), который из Stream<List<T>> сделал бы Stream<Stream<T>>. flatMap сразу возвращает Stream<T>.