I'm creating a list and fill it from an other list using parallel stream, unexpectedly destination list contains nulls. It happens seldom and inconstantly. Does someone have the same issue?
Here is the piece of code:
Collection<DestinationObj> DestinationObjList = Lists.newArrayList();
SourceObjList.parallelStream().forEach(portalRule -> DestinationObjList.add(new DestinationObj(portalRule)));
return DestinationObjList;
You should collect in parallel in a bit different way:
SourceObjList.parallelStream()
.map(DestinationObj::new)
.collect(Collectors.toCollection(ArrayList::new));
The problem you are having is that ArrayList
is not thread-safe and as such the result is really un-defined.
Notice that using a parallel stream does not require a thread-safe collection - Lists::newArrayList
is not.
Using a collector to synchronize access to the destination list gives you a performance penalty in the synchronization. In fact, you can do the same thing without synchronization, since you know the size of the source list and can therefore create a destination list of the required size from the start.
DestinationObj[] dest = new DestinationObj[sourceObjList.size()];
IntStream.range(0, sourceObjList.size())
.parallel()
.forEach(i -> dest[i] = new DestinationObj(sourceObjList.get(i)));
List<DestinationObj> destinationObjList = Arrays.asList(dest);
EDIT: Just putting Holger's improvement here for clarity:
List<DestinationObj> destinationObjList = Arrays.asList(
sourceObjList
.parallelStream()
.map(DestinationObj::new)
.toArray(DestinationObj[]::new));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With