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() ?
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.