Java Lists alternate for nested forEach

Issue

Below is my working code set of nested forEach. Looking for the best alternate to make code clean. Looks to be a very complex set of code

This Model1 is a List containing various SubList and other few elements

List<Model1> listModel1  getDataForModel1(......);
listModel1.getSubList1().forEach(dataSubList1 -> {
    dataSubList1.getChildSublList2().forEach(dataChildSubList2 -> {
        dataChildSubList2.getChildSubList3().forEach(dataChildSubList3 -> {
          if (dataChildSubList3.getIsValid()) {
              // Setting my required data in this dataChildSubList3 all properties
            }
        })
    })
})

The above code is working as expected but Looking for an alternate to forEach. Thus making the above code less complex. Thanks in advance

Solution

This answer should be seen as a sketch. Since the code does not explicitly define the return types, the code might not work 1:1.


In the following, I assume that:

  • listModel1.getSubList1() returns a List<Data1>,
  • dataSubList1.getChildSublList2() returns a List<Data2>, and
  • dataSubList2.getChildSublList3() returns a List<Data3>.

If we use Java 8+, we can utilize the stream API:

List<Model1> listModel1  getDataForModel1(......);
listModel1.getSubList1().stream()

    // 1st block replaces 1st foreach:
    .filter(Objects::nonNull)  // remove nulls, just to be safe
    .map(Data1::getChildSublList2) // transform each Data1-object to its list of 
                                   // Data2-objects
    .flatMap(List::stream) // flatten: transform each list in a stream of its 
                           // elements

    // 2nd block replaces 2nd foreach:
    .filter(Objects::nonNull) // remove nulls, just to be safe
    .map(Data2::getChildSublList3) // transform each Data2-object to its list of 
                                   // Data3-objects
    .flatMap(List::stream) // flatten: transform each list in a stream of its 
                           // elements

    // 3rd block replaces 3rd foreach:
    .filter(Objects::nonNull) // remove nulls, just to be safe
    .filter(Data3::getIsValid) // move if-condition to here
    .forEach(dataChildSubList3 -> {
        // Setting my required data in this dataChildSubList3 all properties
    });

It would be even better if we move the lambda-body of the forEach-lambda in a separate method (e.g. named foo) that takes a Data3 as parameter and does the processing, this would simplify the forEach(...) to:

listModel1.getSubList1().stream()
    ...
    .forEach(this::foo);

Answered By – Turing85

Leave a Comment