0

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.

3
  • If these are purely imperative things, then yes, it's going to look exactly the same. Streams are for transforming and filtering and getting out new elements, generally. Commented Apr 13, 2016 at 21:56
  • 1
    Yep, @Pillar link is basically what you need. .flatMap(outer -> owner.getInnerList().stream().map(inner -> new Object[] { outer, inner })) The issue is always having a Tuple class for holding both inner and outer. Here since they are both Object, we could abuse an array... All in all: keep the for loop. Commented Apr 13, 2016 at 21:56
  • Ok, thanks Pillar and Tunaki. Kind of sucks that there isn't a significantly cleaner way to write it. Commented Apr 13, 2016 at 21:59

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.