Ответ
Метод map в Stream<T> ожидает аргумент типа Function<? super T, ? extends R>.
Разбор интерфейса:
T— тип элемента исходного потока (контравариантный аргумент).R— тип элемента результирующего потока (ковариантный результат).- Единственный абстрактный метод (SAM) интерфейса
Function—R apply(T t).
Суть операции: Преобразование каждого элемента потока из типа T в тип R по правилу, заданному функцией (отображение 1:1).
Пример: Преобразование строк в их длины
List<String> names = List.of("John", "Alice", "Bob");
List<Integer> nameLengths = names.stream()
// Function<String, Integer>: строка -> её длина
.map(String::length)
.toList(); // Результат: [4, 5, 3]
Аналоги в других API:
Optional<T>:Optional.map(Function<T, R>)преобразует значение внутриOptional, если оно присутствует.CompletableFuture<T>:thenApply(Function<T, R>)для синхронного преобразования результата асинхронной операции.
Важно: map — это промежуточная операция, она не вызывает выполнения потока и возвращает новый ленивый поток с преобразованными элементами.
Ответ 18+ 🔞
Слушай, вот этот самый map в стримах — это ж классика, блядь! Представь себе конвейер, по которому едут какие-то хуёвинки типа T. А map — это такой станок, который каждую хуёвинку T хватает, переделывает в другую хуёвинку R и кладёт на следующий конвейер. Одна штука на вход — одна, но уже другая, на выход. Просто, как три копейки, ёпта!
А сигнатура у него, конечно, пиздец какая страшная: Function<? super T, ? extends R>. Но если разобраться, то всё логично, блядь.
? super T— значит, можно запихнуть функцию, которая жрёт не только конкретноT, но и его предков. Хуй с ним, пусть жрёт.? extends R— значит, функция может возвращать не толькоR, но и его детей. Ну и ладно, главное, что вRоно потом влезет.
А внутри у этой функции один-единственный метод apply(T t), который и делает всю магию. Всё, больше от тебя ничего не надо, блядь.
Вот смотри, как это на практике выглядит. Берём список имён и делаем из них список длин этих имён. Ебать мои старые костыли, какая же это частая операция!
List<String> names = List.of("John", "Alice", "Bob");
List<Integer> nameLengths = names.stream()
// Вот тут и живёт наша функция: берёт строку, выёбывается с ней, возвращает число
.map(String::length)
.toList(); // И получаем мы, сука, [4, 5, 3]
И ведь эта штука, map, она везде пролезла, как хитрая жопа! Ты глянь:
- В
Optional<T>— там тоже естьmap, который ковыряется в значении, если оно внутри есть. Нет значения — и похуй, ничего не делает. - В
CompletableFuture<T>— тамthenApplyэто тот же самыйmap, только для асинхронных пиздюлей. Дождался результата — примени функцию.
И главное, запомни: map — это операция-лентяй, промежуточная. Она не запускает весь этот конвейер в пизду. Она просто говорит: «Ладно, я запомнил, что надо будет каждую хуйню преобразовать вот так». А реальная работа начнётся только когда ты позовёшь какую-нибудь терминальную операцию вроде toList() или collect. Вот тогда-то всё и завертится, блядь!