Reputation: 1246
I have a nested for loop I'd like to replace with streams, but I can't figure out how to, if it's possible. The basic loop is below
for (Object outer : owner.getOuterList())
for (Object inner : owner.getInnerList())
if (cond(outer, inner))
func(outer, inner);
else
moo(outer, inner);
The only solution I've been able to arrive is basically looks exactly the same using forEach
instead of the loops. flatMap
seems applicable but I can't figure out how to get both lists.
Upvotes: 0
Views: 390
Reputation: 298123
The main problem is that the inner loop body
if (cond(outer, inner))
func(outer, inner);
else
moo(outer, inner);
has no counterpart in the Stream API and remains a single opaque action for most thinkable solutions, so the simplest solution is to express it as a Consumer
which you invoke using a nested forEach
, which offers no benefit to the nested for
loop you already have.
If you want to enforce a flatMap
based solution, you can do the following:
owner.getOuterList().stream().flatMap(
outer -> owner.getInnerList().stream().<Runnable>map(
inner -> cond(outer, inner)? () -> func(outer, inner): () -> moo(outer, inner))
).forEach(Runnable::run);
It doesn’t need a Pair/Tuple type for mapping to [outer,inner]
as the functions themself are capable of capturing these values. Note that this will evaluate the condition in the inner stream processing but the actual invocation of either func
or moo
will happen in the outer stream processing. You can enforce processing everything within the outer stream with
owner.getOuterList().stream().flatMap(
outer -> owner.getInnerList().stream().<Runnable>map(
inner -> () -> { if(cond(outer,inner)) func(outer,inner); else moo(outer,inner); })
).forEach(Runnable::run);
which, as said, treats your original inner loop’s body like an opaque action.
I think, it’s clear that neither is a real win over the original nested for
loop here.
Upvotes: 1