10

I want to iterate nested lists using java8 streams, and extract some results of the lists on first match. Unfortunately I have to also get a values from the parent content if a child element matches the filter.

How could I do this?

java7

Result result = new Result();

//find first match and pupulate the result object.
for (FirstNode first : response.getFirstNodes()) {
    for (SndNode snd : first.getSndNodes()) {
        if (snd.isValid()) {
            result.setKey(first.getKey());
            result.setContent(snd.getContent());
            return;
        }
    }
}

java8

 response.getFirstNodes().stream()
        .flatMap(first -> first.getSndNodes())
        .filter(snd -> snd.isValid())
        .findFirst()
        .ifPresent(???); //cannot access snd.getContent() here
1

2 Answers 2

13

When you need both values and want to use flatMap (as required when you want to perform a short-circuit operation like findFirst), you have to map to an object holding both values

response.getFirstNodes().stream()
  .flatMap(first->first.getSndNodes().stream()
    .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
  .filter(e->e.getValue().isValid())
  .findFirst().ifPresent(e-> {
    result.setKey(e.getKey().getKey());
    result.setContent(e.getValue().getContent());
  });

In order to use standard classes only, I use a Map.Entry as Pair type whereas a real Pair type might look more concise.

In this specific use case, you can move the filter operation to the inner stream

response.getFirstNodes().stream()
  .flatMap(first->first.getSndNodes().stream()
     .filter(snd->snd.isValid())
     .map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
  .findFirst().ifPresent(e-> {
    result.setKey(e.getKey().getKey());
    result.setContent(e.getValue().getContent());
  });

which has the neat effect that only for the one matching item, a Map.Entry instance will be created (well, should as the current implementation is not as lazy as it should but even then it will still create lesser objects than with the first variant).

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

2 Comments

Thanks for the explaination. Looking at your code I feel it's still better to stick stick to the java7 fashioned iteration style in case multiple/parent elements have to be accessed.
Yeah, the good old for loops aren’t outdated. Maybe a future Java version with language support for pairs/tuples opens the possibility for a nicer stream solution…
-1

It should be like this:

Edit: Thanks Holger for pointing out that the code won't stop at the first valid FirstNode

response.getFirstNodes().stream()
  .filter(it -> {it.getSndNodes().stream().filter(SndNode::isValid).findFirst(); return true;})
  .findFirst()
  .ifPresent(first -> first.getSndNodes().stream().filter(SndNode::isValid).findFirst().ifPresent(snd -> {
    result.setKey(first.getKey());
    result.setContent(snd.getContent());
  }));

A test can be found here

5 Comments

This doesn’t do anything useful. The question’s original code called result.setKey(first.getKey()); result.setContent(snd.getContent()); and stopped processing subsequent items. Your code does nothing like that.
I was showing how to access snd.getContent() within ifPresent(), have modified the code now
@Holger can I get the -1 off please, I have tested that my code works and does what the op intended
I didn’t downvote so I can’t do anything about it. Besides that, using forEach still doesn’t solve the question’s task which is to stop after the first match. Of course, testing with only one matching item won’t exhibit this behavior…
No problems, thanks for pointing mistakes in my code. I have now fixed the code to do the right thing. I'll leave the answer out there because it's correct. Whoever thought the answer is worth downvoting, it would have helped if I was told the reason.

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.