Пример Java Stream mapMulti()

Начиная с Java 16, мы можем использовать метод Stream::mapMulti() для создания нуля, одного или нескольких элементов для каждого входного элемента потока. Метод mapMulti() выглядит довольно похоже на метод flatMap(), но они оба отличаются в своих вариантах использования.

В этом руководстве по Java Stream API обсуждается синтаксис и цели метода mapMulti() с примерами.

1. Синтаксис

Технически говоря, метод mapMulti() является специализированной формой метода map(), который преобразует каждый элемент потока в несколько выходных элементов или ни в один. В то время как метод map() обычно используется для создания одного выходного элемента для каждого входного элемента в потоке.

Входной элемент -> map() -> Выходной элемент
Входной элемент -> mapMulti() -> Ноль, один или несколько выходных элементов

Вот синтаксис метода mapMulti(), который требует реализации функционального интерфейса BiConsumer. Внутри себя BiConsumer принимает элемент потока 'T', при необходимости преобразует его в тип 'R' и вызывает 'Consumer::accept' нижестоящего потока.

<R> Stream<R> mapMulti(BiConsumer<T, Consumer<R>> downstream)

В коде приложения мы используем метод mapMulti() следующим образом:

stream.mapMulti((inputElement, downstream) -> {// Transformation logic// Pass transformed or mapped elements to the downstream 'Consumer'});

2. Простой пример

Давайте попробуем понять метод mapMulti() на простом примере. В следующем коде мы преобразуем список целых чисел в поток их кратных(квадратов и кубов), тем самым создавая несколько выходных элементов для каждого входного элемента.

import java.util.List;public class MapMultiExample {public static void main(String[] args) {List<Integer> numbers = List.of(1, 2, 3);List<Integer> multiples = numbers.stream().<Integer>mapMulti((num, downstream) -> {downstream.accept(num);downstream.accept(num * num); // Add the square of the numberdownstream.accept(num * num * num); // Add the cube of the number}).toList();System.out.println(multiples); // Output: [1, 1, 1, 2, 4, 8, 3, 9, 27]}}

Мы можем выбрать возврат любого количества выходных элементов по мере необходимости или не возвращать никаких значений вообще.

3. Когда использовать метод mapMulti()

Мы можем использовать метод mapMulti() в различных сценариях, таких как:

3.1 Преобразование данных

Когда мы преобразуем каждый входной элемент в один или несколько выходных элементов на основе определенных условий. Псевдокод можно записать как:

stream.mapMulti((inputElement, downstream) -> {if(condition(inputElement)) {// Transform inputElement into multiple output elementsdownstream.accept(outputElement1);downstream.accept(outputElement2);// ...} else {// Optionally, pass the inputElement as is to the downstream consumerdownstream.accept(inputElement);}})

3.2. Сглаживание вложенных коллекций

Когда у нас есть поток вложенных коллекций, и мы хотим объединить их в один поток после проверки определенных условий.

Мы могли бы использовать операцию flatMap(), но это требует дополнительной операции filter() для исключения ненужных вложенных коллекций. Кроме того, flatmap() создает объект Stream внутренне для каждой вложенной коллекции, таким образом, используя больше памяти, чем mapMulti(). Это важно для понимания разницы между операциями flatMap() и mapMulti().

stream.mapMulti((nestedCollection, downstream) -> {// Iterate over each element in the nested collectionif(condition(inputElement)) { // Optional conditionfor(element : nestedCollection) {if(condition(inputElement)) { // Optional condition// Pass each element to the downstream consumerdownstream.accept(element);}}}})

4. Пример потока mapMulti()

Давайте рассмотрим еще один пример, близкий к реальным приложениям. Предположим, у нас есть список транзакций. Для каждой транзакции нам нужно собрать оповещения о транзакциях. Но если сумма транзакции вывода больше 100, то необходимо также дополнительное оповещение о мошенничестве.

Если мы попытаемся решить эту проблему с помощью методов filter() и map(), мы столкнемся с проблемой, что одна транзакция может быть сопоставлена только с одним событием. Но в нашем случае нам нужно, чтобы события были, если есть дополнительное предупреждение о мошенничестве, когда снятие средств является транзакцией на большую сумму.

Давайте решим задачу, используя метод mapMulti(), который служит именно этой цели, то есть возвращает ноль, один или несколько выходных элементов одного входного элемента.

// Sample list of transactionsList<Transaction> transactions = List.of(new Transaction("T1", 100, TransactionType.DEPOSIT),new Transaction("T2", 50, TransactionType.WITHDRAWAL),new Transaction("T3", 200, TransactionType.WITHDRAWAL));// Process transactions and generate transaction eventsList<TransactionEvent> transactionEvents = transactions.stream().mapMulti((transaction, downstream) -> {if(transaction.getType() == TransactionType.DEPOSIT) {downstream.accept(new TransactionEvent("Deposit", transaction.getAmount()));} else if(transaction.getType() == TransactionType.WITHDRAWAL) {downstream.accept(new TransactionEvent("Withdrawal", transaction.getAmount()));// If withdrawal amount is greater than 100, generate an additional fraud alert eventif(transaction.getAmount() > 100) {downstream.accept(new TransactionEvent("Fraud Alert", transaction.getAmount()));}}}).collect(Collectors.toList());// Print the generated transaction eventstransactionEvents.forEach(System.out::println);

Проверьте сгенерированные события:

TransactionEvent{type='Deposit', amount=100.0}TransactionEvent{type='Withdrawal', amount=50.0}TransactionEvent{type='Withdrawal', amount=200.0}TransactionEvent{type='Fraud Alert', amount=200.0}

5. Заключение

В этом коротком руководстве по Java обсуждался метод Stream.mapMulti() с простыми для понимания примерами. Мы обсудили его синтаксис, разницу между методами mapMulti() и map(), разницу между методами mapMulti() и flatMap() и когда использовать метод mapMulti().

Исходный код на Github

Прокрутить вверх