5

I'm not able to convert below snippet in Java 8 stream format.

List<String> titles = Arrays.asList("First Name", "Last Name");

for (FirstClass first : firstClassList) {
    for (SecondClass second : first.getSecondClassList()) {
        for (ThirdClass third : second.getThirdClassList()) {                   

            if(!titles.contains(third.getField())) {
                second.getThirdClassList().remove(third);
            }

        }
    }
}  

I'm comparing third level nested list object against the input list of fields. If fields are not matching then I'm removing them from original list. How can I achieve this using Java 8 syntax.

Edit: I want List of FirstClass to be returned.

3
  • Stream#flatMap and Stream#filter methods could help for this scenario Commented Apr 22, 2019 at 10:17
  • To use streams instead of the for loops you want to flatten the data-structure, i.e. something like firstClassList.stream().flatMap(FirstClass::getSecondClassList() and similar for the second level. Commented Apr 22, 2019 at 10:17
  • This code doesn't work, it throws ConcurrentModificationException Commented Apr 22, 2019 at 15:21

4 Answers 4

3

I don't think streams win you anything in this case. All you do is iterate over the nested lists and either the enhanced for loop or forEach is more straightforward.

The improvements can come from using removeIf to modify the list and, possibly, from moving the rejection logic out of the loop:

Predicate<ThirdClass> reject = third -> !titles.contains(third.getField());

firstClassList.forEeach(first ->
    first.getSecondClassList().forEach(second ->
        second.getThirdClassList().removeIf(reject)
    )
);
Sign up to request clarification or add additional context in comments.

Comments

2

First you can use Stream.map() and Stream.flatMap() to get a Stream containing a List of ThirdClass. To remove the items matching the condition you could use Collection.removeIf(), which removes all items from a collection matching the given condition:

firstClassList.stream()                         // Stream<FirstClass>
        .map(FirstClass::getSecondClassList)    // Stream<List<SecondClass>>
        .flatMap(Collection::stream)            // Stream<SecondClass>
        .map(SecondClass::getThirdClassList)    // Stream<List<ThirdClass>>
        .forEach(thirdList -> thirdList.removeIf(third -> !titles.contains(third.getField())));

This modifies the original List, just like you did in your example. You then can use firstClassList as result for further processing.

Beside that I would recommend using a Set<String> instead of a List<String> for your titles, because it has a time complexity of O(1) instead of O(n):

Set<String> titles = new HashSet<>(Arrays.asList("First Name", "Last Name"));

Comments

1

Get a stream of SecondClass objects by flattening the firstClassList and for each SecondClass get the filtered list of ThirdClass objects and set it back in the SecondClass

List<String> titles = Arrays.asList("First Name", "Last Name");

firstClassList
    .stream()
    .flatMap(firstClass -> firstClass.getSecondClassList().stream())
    .forEach(secondClass -> {
             List<ThirdClass> filteredThirdClasses = secondClass.getThirdClassList()
                        .stream()
                        .filter(thirdClass -> titles.contains(thirdClass.getField()))
                        .collect(toList());

             secondClass.setThirdClassList(filteredThirdClasses);
         }
    );

Comments

0

Assuming the three levels in your data-structure are collections then a solution could be:

  1. flatten the structure to a stream of the leaf level
  2. filter by the required titles
final List<ThirdClassList> results = firstClassList
    .stream()
    .flatMap(FirstClassList::getSecondClassList)
    .flatMap(FirstClassList::getThirdClassList)
    .filter(third -> !titles.contains(third))
    .collect(toList());

This will give you the leaf level objects to be removed, though this is only half the solution of course, you still want to remove them.

If you are the author of the list classes then perhaps you could have a reference from each third level to its 'parent' second level so removing is then a relatively simple second step:

results.forEach(third -> third.parent().remove(this));

where third.parent() returns the second level object.

2 Comments

For performance, you could change the intermediate List to Stream#iterator() and Iterator#remove() - as long as the final Stream supports element removal.
stridecolossus, thanks for your response. But I need FirstClass List instead of leaf.

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.