1)如何使用供应商(find_program()
)并行创建大小的N值流,同时确保向供应商发出不超过N个调用?我需要这个,因为我有一个供应商需要进行昂贵的supplier
操作。
2)显而易见的'回答我的问题,supplier.get()
,不起作用,并且经常导致向供应商发出超过N个调用。这是为什么?
作为'证据' Streams.generate(supplier).limit(N)
导致超过N次调用Streams.generate(supplier).limit(N)
的事实,请考虑以下代码:
supplier.get()
public class MWE {
static final int N_ELEMENTS=100000;
static Supplier<IntSupplier> mySupplier = () -> new IntSupplier() {
AtomicInteger ai = new AtomicInteger(-1);
@Override
public int getAsInt() {
return ai.incrementAndGet();
}
};
public static void main(String[] args) {
int[] a = IntStream.generate(mySupplier.get()).limit(N_ELEMENTS).toArray();
int[] b = IntStream.generate(mySupplier.get()).parallel().limit(N_ELEMENTS).toArray();
}
}
按预期等于a
,但与您预期的相反[0, 1, ..., N_ELEMENTS-1]
不包含与b
相同的元素。相反,a
通常包含大于或等于b
的元素,这表示对供应商的调用次数超过N_ELEMENTS
。
另一个例子是N_ELEMENTS
并不总是生成相同的数字集。
答案 0 :(得分:4)
流API不保证IntStream.generate()
将指定次数调用生成器。此电话也不尊重订购。
如果您确实需要增加数字的并行流,那么使用IntStream.range(0, N_ELEMENTS).parallel()
会好得多。这不仅可以确保您实际拥有从0
到N_ELEMENTS-1
的所有数字,还可以大大减少争用并保证顺序。如果您需要生成更复杂的内容,请考虑使用定义您自己的Spliterator类的自定义源。
请注意,建议的IntStream.iterate
解决方案可能不会大规模并行化,因为它是按顺序排列的。
答案 1 :(得分:1)
不保证调用.limit()
会导致供应商生成前N个元素的流,因为Stream.generate()
会创建一个unordered流,limit()
可以自由发送决定什么&#39; part&#39;要保持的流。实际上,参考&#34;前N个元素&#34;甚至在语义上都不合理。或&#34;(#)的第一部分&#34;,因为流是无序的。这种行为在API文档中清楚地列出;非常感谢所有向我指出这一点的人!
自从提出这个问题以来,我已经为自己的问题提出了两个解决方案。我要感谢Tagir,他让我朝着正确的方向前进。
IntStream.range()
一种简单而有效的方法来创建一个供应商支持的无序,大小的并行流,该供应商不再向供应商发出绝对必要的调用,而是(误)使用IntStream.range()
这样:
IntStream.range(0,N_ELEMENTS).parallel().mapToObj($ -> generator.get())
基本上,我们仅使用IntStream.range()
来创建可以并行处理的大小的流。
因为我们从未实际使用IntStream.range()
创建的流内部的整数,所以我们可以通过创建自定义Spliterator来做得更好:
final class SizedSuppliedSpliterator<T> implements Spliterator<T> {
private int remaining;
private final Supplier<T> supplier;
private SizedSuppliedSpliterator(Supplier<T> supplier, int remaining) {
this.remaining = remaining;
this.supplier = supplier;
}
static <T> SizedSuppliedSpliterator of(Supplier<T> supplier, int limit) {
return new SizedSuppliedSpliterator(supplier, limit);
}
@Override
public boolean tryAdvance(final Consumer<? super T> consumer) {
Objects.requireNonNull(consumer);
if (remaining > 0) {
remaining--;
final T supplied = supplier.get();
consumer.accept(supplied);
return true;
}
return false;
}
@Override
public void forEachRemaining(final Consumer<? super T> consumer) {
while (remaining > 0) {
consumer.accept(supplier.get());
remaining--;
}
}
@Override
public SizedSuppliedSpliterator<T> trySplit() {
int split = (int)remaining/2;
remaining -= split;
return new SizedSuppliedSpliterator<>(supplier, split);
}
@Override
public long estimateSize() {
return remaining;
}
@Override
public int characteristics() {
return SIZED | SUBSIZED | IMMUTABLE;
}
}
我们可以使用此spliterator来创建流,如下所示:
StreamSupport.stream(SizedSuppliedSpliterator.of(supplier, N_ELEMENTS), true)
当然,计算几个整数并不昂贵,而且我还没有注意到甚至没有考虑到解决方案1的性能改进。