Ответ
Оба метода являются промежуточными операциями 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>.
Ответ 18+ 🔞
А, слушай, смотри, тут два таких метода, как map и flatMap. Вроде оба из Stream API, оба промежуточные, но работают, блядь, по-разному, как кот и собака, понимаешь? Один просто меняет, а второй — выворачивает наизнанку, как карман у бомжа.
map — это как взял один предмет, хуяк — и превратил его в другой, один в один. Кинул в мясорубку яблоко — получил яблочное пюре. Одно яблоко — одна порция пюре. Формула, блядь, простая: 1 → 1.
flatMap — это уже цирк, ёпта. Ты берёшь предмет, а он, сука, внутри себя содержит кучу других предметов, как матрёшка, блядь, или как пиздопроебибный список списков. Ты его не просто меняешь, ты его распаковываешь нахуй. Берёшь один элемент, делаешь из него целый поток (Stream) новых элементов, а потом все эти потоки склеиваешь в один большой. Формула: 1 → много → 1 (склеенный).
Вот смотри на примеры, тут всё понятно станет.
map — взяли имена, получили их длины. Ничего не разворачивали, просто преобразовали.
List<String> names = List.of("Anna", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length) // Каждую строку -> в её длину (число)
.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) // Берем каждый внутренний List, делаем из него Stream, и все Stream'ы склеиваем!
.toList(); // ["a", "b", "c", "d", "e"]
Где flatMap реально выручает, блядь?
- Optional'ы заебали. Есть
Stream<Optional<User>>, а тебе нужен простоStream<User>без этих пустых коробок.flatMap(Optional::stream)— и оп-па, только живые юзеры остались. - Со строками. Допустим, split'ишь строку — получаешь массив.
map(s -> s.split(" "))даст тебеStream<String[]>, а это, блядь, неудобно. А вотflatMap(s -> Arrays.stream(s.split(" ")))— уже красота, плоскийStream<String>со всеми словами подряд.
Короче, map — когда меняешь форму одного в один. flatMap — когда нужно выковырять содержимое из внутренних коллекций и склеить всё в одну кучу. Всё, вопрос закрыт, иди работай.