简体   繁体   中英

How to parallelize a loop in Java

In the following code, a local method is called on every element of a HashSet. If it returns a special value we halt the loop. Otherwise we add every return value to a new HashSet.

HashSet<Object> myHashSet=…; 
HashSet<Object> mySecondHashSet=…; 

for (Object s : myHashSet) {
    Object value = my_method(s);
    if(value==specialValue)
        return value; 
    else 
        mySecondHashSet.add(value);
 }

I'd like to parralelize this process. None of the objects in the HashSet have any objects in common (it's a tree-like structure) so I know they can run without any synchonization issues. How do I modify the code such that each call of my_method(s) starts a new tread, and also that if one of the threads evaluates to the special values, all the threads halt without returning and the special value is returned?

Having in mind java 8, this could be relatively simple, while it won't preserve your initial code semantics:

In case all you need is to return special value once you hit it

if (myHashSet.parallelStream()
             .map(x -> method(x))
             .anyMatch(x -> x == specialValue)) {

    return specialValue;
}

If you need to keep transformed values until you meet the special value, you already got an answer from @Elliot in comments, while need to mention that semantic is not the same as your original code, since no orderer will be preserved.


While it yet to be checked, but I would expect following to be optimized and stop once it will hit wanted special value:

if (myHashSet.parallelStream()
             .anyMatch(x -> method(x) == specialValue)) {

    return specialValue;
}

I would do that in two passes:

  1. find if any of the transformed set elements matches the special value;
  2. transform them to a Set.

Starting a new thread for each transformation is way too heavy, and will bring your machine to its knees (unless you have very few elements, in which case parallelizing is probably not worth the effort.

To avoid transforming the values twice with my_method , you can do the transformation lazily and memoize the result:

private class Memoized {
    private Object value;
    private Object transformed;
    private Function<Object, Object> transform;

    public Memoized(Object value, Function<Object, Object> transform) {
        this.value = value;
    }

    public Object getTransformed() {
        if (transformed == null) {
            transformed = transform.apply(value);
        }
        return transformed;
    }
}

And then you can use the following code:

Set<Memoized> memoizeds = 
    myHashSet.stream() // no need to go parallel here
             .map(o -> new Memoized(o, this::my_method))
             .collect(Collectors.toSet());

Optional<Memoized> matching = memoized.parallelStream()
    .filter(m -> m.getTransformed().equals(specialValue))
    .findAny();

if (matching.isPresent()) {
    return matching.get().getTransformed();
}

Set<Object> allTransformed = 
    memoized.parallelStream() 
            .map(m -> m.getTransformed())
            .collect(Collectors.toSet());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM