如何在 Java 中并行化循环



在下面的代码中,对 HashSet 的每个元素调用一个本地方法。 如果它返回一个特殊值,我们将停止循环。否则,我们将每个返回值添加到新的 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);
}

我想把这个过程说出来。HashSet 中的所有对象都没有任何共同的对象(它是一个树状结构(,所以我知道它们可以在没有任何同步问题的情况下运行。如何修改代码,以便每次调用 my_method 启动一个新的踏步,并且如果其中一个线程的计算结果为特殊值,则所有线程都停止而不返回并返回特殊值?

考虑到Java 8,这可能相对简单,但它不会保留您的初始代码语义:

如果您只需要在击中它后返回特殊值

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

如果您需要保留转换后的值直到满足特殊值,那么您已经在注释中得到了@Elliot的答案,同时需要提及语义与原始代码不同,因为不会保留任何排序器。


虽然它尚未检查,但我希望以下内容得到优化并在达到想要的特殊价值后停止:

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

我会分两次这样做:

  1. 查找是否有任何转换后的集合元素与特殊值匹配;
  2. 将它们转换为集合。

为每个转换启动一个新线程太繁重了,并且会使您的机器瘫痪(除非您的元素很少,在这种情况下,并行化可能不值得付出努力。

为了避免使用my_method将值变换两次,您可以懒惰地进行变换并记住结果:

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;
}
}

然后,您可以使用以下代码:

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());

最新更新