Notice that the kind of type inference required to deduce the resulting stream type when you use flatMap
, is very different from that when you use mapMulti
.
When you use flatMap
, the type of the resulting stream is the same type as the return type of the lambda body. That's a special thing that the compiler has been designed to infer type variables from (i.e. the compiler "knows about" it).
However, in the case of mapMulti
, the type of the resulting stream that you presumably want can only be inferred from the things you do to the consumer
lambda parameter. Hypothetically, the compiler could be designed so that, for example, if you have said consumer.accept(1)
, then it would look at what you have passed to accept
, and see that you want a Stream<Integer>
, and in the case of getItems().forEach(consumer)
, the only place where the type Item
could have come from is the return type of getItems
, so it would need to go look at that instead.
You are basically asking the compiler to infer the parameter types of a lambda, based on the types of arbitrary expressions inside it. The compiler simply has not been designed to do this.
Other than adding the <Item>
prefix, there are other (longer) ways to let it infer a Stream<Item>
as the return type of mapMulti
:
Make the lambda explicitly typed:
var items = users.stream()
.mapMulti((User u, Consumer<Item> consumer) -> u.getItems().forEach(consumer))
.collect(Collectors.toSet());
Add a temporary stream variable:
// By looking at the type of itemStream, the compiler can figure out that mapMulti should return a Stream<Item>
Stream<Item> itemStream = users.stream()
.mapMulti((u, consumer) -> u.getItems().forEach(consumer));
var items = itemStream.collect(Collectors.toSet());
I don't know if this is more "simplified", but I think it is neater if you use method references:
var items = users.stream()
.map(User::getItems)
.<Item>mapMulti(Iterable::forEach)
.collect(Collectors.toSet());
flatMap
in a very many places. According to multiMap's javadoc"When replacing each stream element with a small (possibly zero) number of elements."
it can provide better performance. Indeed. I only replace each stream element with a small (possibly zero) number of elements.. so it would fit for me to usemultiMap
.. – LukashJava MircobenchMark Harness
. As long as you want to make improvements, you might also find other areas unrelated to flatMap that could use some tweaking. – ReverberatemapMulti
overflatMap
, which is that it is more amenable to "imperative" generation of the replacement. WithflatMap
, you'd have to generate them into aList
, and then ask theList
for aStream
; withmapMulti
, you can generate them directly into the next stage of the pipeline. – Sackbut