3

I want to extract a collection of objects from another collection. The objects to be filtered must be a specific type (or subtype) and must intersect with a given Shape. I want to do it with parallelStream

I have the following code:

public class ObjectDetector {
...
    public ObjectDetector(final Collection<WorldObject> objects,
        final BiFunction<Shape, Shape, Boolean> isIntersecting) {
    ...
    }

    public List<ISensor> getSonarObjects(final Shape triangle) {
        return selectIntersecting(triangle, ISensor.class);
    }

    private <T> List<T> selectIntersecting(Shape triangle, Class<T> type) {
        return objects.parallelStream()
                .filter(o -> type.isInstance(o) && isIntersecting.apply(o.getShape(), triangle))
                .map(o -> type.cast(o)).collect(Collectors.toList());

The problematic part is in the List<T> selectIntersecting(Shape triangle, Class<T> type) method, in which objects is a Collection and isIntersecting is a BiFunction<Shape,Shape,Boolean>.

When I'm using stream() instead of parallelStream() all my tests are green. So I may assume that the filtering and mapping logic works fine. However when I am trying to use the parallelStream() my tests are failing unpredictably. The only coherence that I was able to observe is that the size() of the returned List<T> is less than or equal to (but of course never greater) the size I expect.

A failing testcase for example:

int counter = 0;
public BiFunction<Shape, Shape, Boolean> every2 = (a, b) -> {
    counter++;
    return counter % 2 == 0 ? true : false;
};

@Test
public void getEvery2Sonar() {
    assertEquals("base list size must be 8",8,list.size());
    ObjectDetector detector = new ObjectDetector(list, every2);
    List<ISensor> sonarable = detector.getSonarObjects(triangle);
    assertEquals("number of sonar detectables should be 3", 3, sonarable.size());
}

And the test result is:

Failed tests: getEvery2Sonar(hu.oe.nik.szfmv.environment.ObjectDetectorTest): number of sonar detectables should be 3 expected:<3> but was:<2>

In my understanding - as it is written here - it is possible to collect a parallelStream into non-concurrent Collection.

I've also tried to find some clues on the Parallelism tutorial page, but I'm still clueless.

Could someone please provide me an explanation about what am I doing wrong?

1 Answers1

3

Your predicate function has side effects - this is going to go badly with parallelStream because the evaluation order across the input stream is non-deterministic, plus you have no locking on your mutable state.

Indeed, the documentation for filter states* that the predicate must be stateless.

I'm not sure what behaviour you're trying to achieve here, so I'm not sure what an appropriate "fix" might be.


* No pun intended.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Thanks @Oliver Charlesworth! You are right. In fact, I totally lost sight of that this specific predicate causes a side effect by incrementing a counter. That test case is wrong. I was intended to test the Shape intersection inspection logic (it has no side effect) and the stream processing separately. So I was trying to write as simple predicates as possible i.e. true for all, true for none, true for every second... – Kálmán Kostenszky Nov 05 '17 at 19:53