Java Stream mapMulti() Example

Java 16 onwards, we can use Stream::mapMulti() method to produce zero, one, or multiple elements for each input element of the stream. The mapMulti() method looks quite similar to flatMap() method but they both differ in their usecases.

This Java Stream API tutorial discusses the mapMulti() method syntax and purposes with examples.

1. Syntax

Technically speaking, the mapMulti() method is a specialized form of the map() method that transforms each element of a stream into multiple output elements or none at all. Whereas map() method generally is used to produce a single output element for each input element in the stream.

Input element -> map() -> Output element
Input element -> mapMulti() -> Zero, one or multiple output elements

Here’s the syntax of the mapMulti() method which requires a BiConsumer functional interface implementation. Internally, the BiConsumer takes a Stream element ‘T’, if necessary, transforms it into type ‘R’, and invokes the downstream’s ‘Consumer::accept’.

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

In the application code, we use the mapMulti() method as follows:

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

2. A Simple Example

Let’s try to understand the mapMulti() method with a simple example. In the following code, we are transforming a list of integers into a stream of their multiples (squares and cubes), thus producing multiple output elements for each input element.

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 number
          downstream.accept(num * num * num); // Add the cube of the number
        })
        .toList();

    System.out.println(multiples); // Output: [2, 3, 4, 6, 6, 9, 8, 12, 10, 15]
  }
}

We can choose to return any number of output elements as required, or not return any value at all.

3. When to use mapMulti() Method

We can use the mapMulti() method in various scenarios such as:

3.1. Data Transformation

When we transform each input element into one or multiple output elements based on certain conditions. The pseudo-code can be written as:

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

3.2. Flattening Nested Collections

When we have a stream of nested collections and we want to flatten them into a single stream, after checking certain conditions.

We could have used flatMap() operation but that requires an additional filter() operation for excluding the non-required nested collections. Also, flatmap() creates a Stream object internally for each nested collection, thus using more memory than mapMulti(). This is important to understand the difference between flatMap() and mapMulti() operations.

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

4. Stream mapMulti() Example

Let us take another example close to real-world applications. Suppose we have a list of transactions. For each transaction, we have to collect the transaction alerts. But if the withdrawal transaction amount is greater than 100, then an additional fraud alert is also needed.

If we try to solve this problem using filter() and map() methods, we face the issue that one transaction can be mapped to only one event. But in our case, we need to events if there is an additional fraud alert when withdrawals are greater amount transactions.

Let us solve using the problem with mapMulti() method which serves the exact purpose i.e. returning zero, one, or multiple output elements of a single input element.

// Sample list of transactions
List<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 events
List<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 event
          if (transaction.getAmount() > 100) {
            downstream.accept(new TransactionEvent("Fraud Alert", transaction.getAmount()));
          }
      }
  })
  .collect(Collectors.toList());

// Print the generated transaction events
transactionEvents.forEach(System.out::println);

Verify the generated events:

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. Conclusion

This short Java tutorial discussed the Stream.mapMulti() method with simple-to-follow examples. We discussed its syntax, the difference between mapMulti() and map() methods, the difference between mapMulti() and flatMap() methods, and when to use mapMulti() method.

Happy Learning !!

Source Code on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.