0

I have a List of Rec's and I want to process them all in parallel, by calling some method foo().

foo() may flag some Rec's as erroneous and at the end all the error recs are reported by their index position (e.g. "rec 12 is not valid").
So I want to either pass the index position of each Rec to foo(), or set the index into each Rec before calling foo.
What I attempted to do (shown below) is first, sequentially, set the index to each Rec, and then in parallel call foo on each. Is there a better way to do this?

List<Rec> recs;
class Rec {
    private int recordIndex;
    public void setRecordIndex( int index) { recordIndex = index; }
    //more memebers and getter to the above...
}

//somewhere in the code
int index = 0;
recs.stream().forEach(rec -> rec.setRecordIndex(index++));
//the compiler complains about the index++ above, says it should be final
rec.parallelStream().forEach(rec -> foo(ProgressInfo, rec));

Is there a better way to do this overall? and if not, is there a way to fix the compilation error and still use stream? (instead of looping)

1
  • Is it not using a boolean to set the valid state and collecting at the end a better option? Commented Jul 25, 2018 at 6:29

2 Answers 2

2

This can be done with IntStream, also I would recommend using just one Stream:

IntStream.range(0, recs.size())
    .mapToObj(i -> {
       Rec rec = recs.get(i);
       rec.setRecordIndex(i);
       return rec;
    })
    .parallel()
    .forEach(rec -> foo(ProgressInfo, rec));

Altough it is discouraged to modify a state in streams so instead of setting the index inside the mapToObj it would probably better to return a new object. E.g. something like this:

.mapToObj(i -> {
   Rec copy = new Rec(recs.get(i)); // copy constructor
   copy.setRecordIndex(i);
   return copy;
})

Depending on the List implementation you're using (ArrayList performs better than LinkedList when using index-access), you could also use following snippet. But the use of peek in productive code is a bit controversial.

IntStream.range(0, recs.size())
   .peek(i -> recs.get(i).setRecordIndex(i))
   .parallel()
   .forEach(i -> foo(ProgressInfo, recs.get(i));
Sign up to request clarification or add additional context in comments.

1 Comment

There is no fundamental difference in the snippets regarding the List implementation, all of your variants call get(int), Whether you run it once per element or twice as in your last snippet, doesn’t make much difference. Regarding the side effects, there’s also not much difference between doing them in a map operation or within a peek. Creating a new object like in your second variant is the cleanest option, but keep in mind that the OP said, passing the index to foo is also ok, i.e. IntStream.range(0, recs.size()) .parallel() .forEach(ix -> foo(progressInfo, ix, recs.get(ix)))
1

You can use an IntStream:

IntStream.range(0, recs.size())
         .forEach(i -> recs.get(i).setRecordIndex(i));

That simply uses a stream of numbers from 0 to recs.size() - 1, and calls recs.get for each of number.

Comments

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.