Search code examples
javajava-8parallel-processingparallel.foreach

How to convert normal for loop to parallel stream in java


I am trying to convert normal for loop iterating an ArrayList to a parallel one. I came across the concepts of parallelStream.forEach() and parallelStream().collect(Collectors.toList()). But not clear how to convert. Below is the sample code for normal for loop :

public List<FinPlan> getFinPlanList() {
List<FinPlan> newList = new ArrayList<>();

// get list of string   
List<String> finPlanIdsList = finPlanSearchRequest.getFinPlanIds();

// iterate list and add element to new list
    for(String planId: finPlanIdsList) {
    
        newList.add(retrieveFinPlan(planId));
    }
    
    return newList;
}

I tried with the following code but its giving me error that ""newList should be final".

    finPlanIdsList.parallelStream().forEach( planId -> {
            newList.add(retrieveFinPlan(planId));
    });

So, the main question is how to convert the above normal loop to parallelStream.forEach() and parallelStream().collect() ?


Solution

  • The compilation error in your current version is happening because a lambda expression is not allowed to make use of a local variable that is not either final or effectively final at the point that the lambda is created.

    You could try fix it like this:

    final List<FinPlan> = new ArrayList<>();
    
    ...
    
    finPlanIdsList.parallelStream().forEach( planId -> {
            newList.add(retrieveFinPlan(planId));
    });
    

    ... but that has the more subtle flaw that the code won't be thread-safe. The newList object is potentially going to be updated from different threads ... and ArrayList is not a thread-safe class.

    A correct solution is this:

    List<FinPlan> newList = 
        finPlanIdsList.parallelStream()
                      .map(planId -> retrieveFinPlan(planId))
                      .collect(Collectors.toList());
    

    If it is essential that the result is an ArrayList, you can replace the Collectors.toList() expression with Collections.toCollection(ArrayList::new).

    In general, it is a bad idea for a stream to rely on side-effects. In some cases you will get away with it. In others ... no.