Ответ
Метод map() в java.util.stream.Stream работает с функциональным интерфейсом java.util.function.Function<T, R>.
Детали интерфейса Function<T, R>:
- Абстрактный метод:
R apply(T t)— принимает аргумент типаTи возвращает результат типаR. - Назначение в
map(): Задать правило преобразования каждого элемента исходного потока (Stream<T>) в элемент нового потока (Stream<R>).
Почему это важно? Использование стандартного интерфейса Function обеспечивает совместимость с лямбда-выражениями и ссылками на методы, что делает код лаконичным.
Пример с лямбдой и ссылкой на метод:
List<String> names = List.of("John", "Alice", "Bob");
// Использование лямбда-выражения
List<String> upperNames1 = names.stream()
.map(name -> name.toUpperCase()) // Function<String, String>
.toList();
// Использование ссылки на метод (более идиоматично)
List<String> upperNames2 = names.stream()
.map(String::toUpperCase) // Тот же Function<String, String>
.toList();
Ключевые свойства map():
- Не модифицирует источник: Работает по принципу immutability.
- Ленивая операция: Вычисление происходит только при вызове терминальной операции (например,
collect()илиtoList()). - Сохраняет порядок: В последовательных потоках порядок элементов сохраняется.
Ответ 18+ 🔞
Ну ты смотри, какой народ-то пошёл! Приходит человек к Stream.map(), а там, блядь, целый интерфейс Function<T, R> торчит, как манда с ушами. И думает: «Ёпта, что за хуйня?». А это, оказывается, не просто так, а специально, чтобы твои мозги не взорвались от овердохуища разных вариантов.
Короче, слушай сюда, распиздяй. Этот Function<T, R> — он как универсальная инструкция: «Вот тебе штука типа T, сделай из неё штуку типа R и отъебись». Всё, что ему надо — это один ёбаный метод R apply(T t). Всё! Больше от тебя нихуя не нужно.
И вся магия в том, что из-за этой простоты можно делать вот такие, с позволения сказать, фокусы:
List<String> names = List.of("John", "Alice", "Bob");
// Вариант для тех, кто любит расписывать всё по полочкам, как будто заявление в ЖЭК пишешь
List<String> upperNames1 = names.stream()
.map(name -> name.toUpperCase()) // Смотри-ка, лямбда! name -> хуяк -> NAME
.toList();
// А это вариант для ленивых гениев, которые поняли, что можно ещё короче
List<String> upperNames2 = names.stream()
.map(String::toUpperCase) // Во, блядь! Ссылка на метод! Красота же, ёпта!
.toList();
Видишь? Оба варианта — это один и тот же Function<String, String>. Просто во втором случае компилятор, хитрая жопа, сам догадывается, что к чему.
А теперь, чтобы ты совсем не обосрался с дуру, запомни три вещи про map():
- Он ничего не портит в исходнике. Взял поток, нахуйчил из него новый, а старый как лежал нетронутым, так и лежит. Immutability, блядь, святое!
- Он ленивый, как мой кот сука собака. Пока ты не скажешь «соберись, падла!» (типа
collect()илиtoList()), он нихуя делать не будет. Просто запомнит, что надо сделать. - Порядок он соблюдает. Если в потоке шли «Вася», «Петя», «Маша», то и после
map()они так и пойдут, только, например, «ВАСЯ», «ПЕТЯ», «МАША». Никакого «Маша, Вася, Петя» не будет, если поток последовательный.
Вот и вся философия. Не так страшен чёрт, как его java.util.function.