在下面的代码中,对 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;
}
我会分两次这样做:
- 查找是否有任何转换后的集合元素与特殊值匹配;
- 将它们转换为集合。
为每个转换启动一个新线程太繁重了,并且会使您的机器瘫痪(除非您的元素很少,在这种情况下,并行化可能不值得付出努力。
为了避免使用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());