51

I have a list of objects A. Each object A in this list contains list of object B and the object B contains list of Object C. The object C contains an attribute name that i want to use to filter using java 8.

how to write the code below in java 8 using streams to avoid nested loop :

C c1 = null;
String name = "name1"
for (A a: listOfAObjects) {
    for (B b: a.getList()) {
        for (C c: b.getPr()) {
            if (c.getName().equalsIgnoreCase(name)) {
                c1= c;
                break;
            }
        }
    }
}
0

4 Answers 4

67

You can use two flatMap then a filter then you can pick the first one or if no result return null :

C c1 = listOfAObjects.stream()
        .flatMap(a -> a.getList().stream())
        .flatMap(b -> b.getPr().stream())
        .filter(c -> c.getName().equalsIgnoreCase(name))
        .findFirst()
        .orElse(null);
Sign up to request clarification or add additional context in comments.

7 Comments

flatMap is eager until java-10, unlike the for loop
@Eugene it still stops at the first matching A, only the processing of the sublists is not short-circuiting.
@sk555 just search flatMap eager, run a few examples and you will see for yourself; but long story short: flatMap will traverse the entire source, your for loops will not
@YCF_L i have tested it in simple example but id t doesn't work with the list i want to use i get always c1 null
@sk555 well if you get a null it probably means that orElse was triggered, in which case it means that there isn't such an element in your list, simple as that.
|
18

You can do it with flatMap.

I made an example with Company which contains a list of Person :

public static void main(String[] args) {
    List<Company> companies = Arrays.asList(
            new Company(Arrays.asList(new Person("Jon Skeet"), new Person("Linus Torvalds"))),
            new Company(Arrays.asList(new Person("Dennis Ritchie"), new Person("Bjarne Stroustrup"))),
            new Company(Arrays.asList(new Person("James Gosling"), new Person("Patrick Naughton")))
    );

    List<String> persons = companies.stream()
            .flatMap(company -> company.getPersons().stream())
            .map(Person::getName)
            .collect(Collectors.toList());

    System.out.println(persons);
}

Output :

[Jon Skeet, Linus Torvalds, Dennis Ritchie, Bjarne Stroustrup, James Gosling, Patrick Naughton]

2 Comments

This is the correct approach, however the result is different: at the end of all nested loops, you should be left with just the last "c" object containing "name" in all the nested lists.
@GDC I agree. To my understanding, the OP's question was "How to make one Stream from 2 nested collections", so I just answered that.
7
 listOfObjectsA.stream()
               .flatMap(a -> a.getListOfObjectsB.stream())
               .flatMap(b -> b.getListOfObjectsC().stream())
               .filter(c -> name.equals(c.getName()))
               .findAny()
               .orElse(....)

2 Comments

whatbis the diffrence with the one specified above ?
@sk555 none really... the difference here is that I've used findAny instead of findFirst - under the current implementation for a sequential stream they will act the same; but a parallel one - you want findFirst probably - as it will get the first element in encounter order. The thing is - if you don't care which to take (as long as it matches your condition) - you probably want findAny
6

I had same task but i had one nested class. And I had to filter the objects with filter in the nested collection. As a result, I had to get items that have matches in the collection.

for example:

public class RootElement {
    private String name;
    private List<String> nestedElements;

    //getters / setters and constructors
}

init collection with elements:

List<RootElement> elements = Arrays.asList(
                new RootElement("first", Arrays.asList("one", "two", "three")),
                new RootElement("second", Arrays.asList("four", "one", "two")));

filter example:

String filterParam = "four";
        List<RootElement> filtered = elements.stream()
                .flatMap(root -> root.getNestedElements()
                        .stream()
                        .filter(nested -> nested.equalsIgnoreCase(filterParam))
                        .map(filteredElement -> new RootElement(root.getName(), root.getNestedElement())))
                .collect(Collectors.toList());

Hope it will help someone.

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.