Press "Enter" to skip to content

Java 16’s Stream#mapMulti() – a Better Stream#flatMap Replacement?

Nowadays, new Java versions get released so fast that small things often get unnoticed and it turns out that Java 16 brought a new interesting addition to Stream API – mapMulti() method that you probably have not heard about yet.

Stream#mapMulti()

I bet you’re excited to look at the new method, so let’s do it then:

<R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper)

At first sight, it can seem a bit… cryptic but at its core mapMulti() is a flatMap()’s alternative. Both are used for processing Stream elements with one-to-many semantics.

However, mapMulti() gives you a chance to consume elements manually using a provided Consumer instance:

var lists = of(of(1), of(2, 3), List.<Integer>of());

var result = lists.stream()
  .mapMulti((list, consumer) -> list.forEach(consumer))
  .collect(Collectors.toList());

And the same implemented with flatMap():

// import static java.util.List.of;
var lists = of(of(1), of(2, 3), List.<Integer>of());
var result = lists.stream()
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

Rationale

if it ain’t broken, don’t fix it.

But what’s wrong with flatMap() that would justify introducing another, and less convenient method to the Stream API?

The answer is: nothing.

However, mapMulti() can be more convenient and more performant for certain edge cases(low number of elements) so you should consider the new method as a complementary addition and not a game-changer. At the end of the day, it delegates the work to flatMap() internally.

The performance gain is achieved by streamlining the construction of Stream instance that gets eventually passed to flatMap(). This is achieved by collecting resulting elements into a SpinedBuffer and then converting it into Stream:

default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
    Objects.requireNonNull(mapper);
    return flatMap(e -> {
        SpinedBuffer<R> buffer = new SpinedBuffer<>();
        mapper.accept(e, buffer);
        return StreamSupport.stream(buffer.spliterator(), false);
    });
}

Sources

Code snippets can be found on GitHub.

Original issue.




If you enjoyed the content, consider supporting the site: