1

I have a class that constructs several foo objects. The problem right now is that the class iterates one by one down a list and builds them. I want to see if there's a way to make all of the building happen in parallel. Here's the class in question:

public List<Foo> create() {}
    List<Class> fooTypes = Arrays.asList(
                    Foo1.class,
                    Foo2.class,
                    Foo3.class,
                    Foo4.class,
                    Foo5.class,
                    Foo6.class
            );

    List<Foo> foos = new ArrayList<>();

    for(Class<? extends Foo> fooType : fooTypes) {
        Optional<Foo> foo = findByType(fooType);
        if(foo.isPresent()) {
            foos.add(foo.get());
        }
    }
    return foos;
}

private Optional<Foo> findByType(Class<? extends Foo> fooClass) {
    if(fooClass.isAssignableFrom(Foo1.class)) {
        return Optional.ofNullable(foo1());
    }
    if(fooClass.isAssignableFrom(Foo2.class)) {
        return Optional.ofNullable(foo2());
    }
    if(fooClass.isAssignableFrom(Foo3.class)) {
        return Optional.ofNullable(foo3());
    }
    if(fooClass.isAssignableFrom(Foo4.class)) {
        return Optional.ofNullable(foo4());
    }
    if(fooClass.isAssignableFrom(Foo5.class)) {
        return Optional.ofNullable(foo5());
    }
    if(fooClass.isAssignableFrom(Foo6.class)) {
        return Optional.ofNullable(foo6());
    }
    return Optional.empty();
}

private Alert foo1() {
    return new Foo1Builder().build();
}
private Alert foo2() {
    return new Foo2Builder().build();
}
private Alert foo3() {
    return new Foo3Builder().build();
}
private Alert foo4() {
    return new Foo4Builder().build();
}
private Alert foo5() {
    return new Foo5Builder().build();
}
private Alert foo6() {
    return new Foo6Builder().build();
}

Is there a way to convert the foreach loop into a java 8 parallel stream? If not, is going the executor route outlined here the correct way to go? I tried something like this to implement parallel streams but something is just not correct:

foos = fooTypes
        .parallelStream()
        .filter(a -> findByType(a).isPresent())
        .map(Optional::get)
        .collect(Collectors.toList());
1
  • Your filter is incorrect: map first, then filter on isPresent. Commented Apr 7, 2016 at 19:00

1 Answer 1

4

A parallel stream will be much less efficient than a sequential stream for such a small number of elements. It's overkill.

But you can use a Stream, indeed:

foos = fooTypes
        .stream()
        .map(this::findByType)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());

That said, I really don't know what you gain by iterating on the classes to find which method to call on each class. Why not just use

return Arrays.asList(new Foo1Builder().build(),
                     new Foo2Builder().build(),
                     ...);

It seems much less convoluted to me.

Sign up to request clarification or add additional context in comments.

6 Comments

i got an error when i tried this. it said Bad return type in method reference: cannot convert Optional<Foo> to R
Your list of classes should be of type List<Class<? extends Foo>>.
i tried changing the list from List<Class> to List<Class<? extends Foo>> and same error. Additionally, if I want to pass in other arguments into findByType how could I do that?
It compiles fine here, with that complete minimal self-contained example: gist.github.com/jnizet/d4192cc0ce33847969af547b4d37dd36. Not sure why it doesn't at your side. Try posting a complete minimal self-contained example reproducing your compilation error.
To pass another argument to findByType, use a lambda rather than a method reference: map(clazz -> findByType(clazz, 1, 2, 3))
|

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.