Java 8 溪流如何停止?
我想知道当我用Java 8 溪流如何停止?,java-8,java-stream,infinite,Java 8,Java Stream,Infinite,我想知道当我用stream.generate创建自己的无限流时,标准库中的流是如何停止的 例如,当您有一个包含记录的列表时: List<Record> records = getListWithRecords(); records.stream().forEach(/* do something */); List records=getListWithRecords(); records.stream().forEach(/*dosomething*/); 流不会是无限的,不会
stream.generate
创建自己的无限流时,标准库中的流是如何停止的
例如,当您有一个包含记录的列表时:
List<Record> records = getListWithRecords();
records.stream().forEach(/* do something */);
List records=getListWithRecords();
records.stream().forEach(/*dosomething*/);
流不会是无限的,不会永远运行,但当遍历列表中的所有项时,它将停止。但这是怎么回事?同样的功能也适用于由文件.lines(路径)
(源代码:)创建的流
还有第二个问题,使用
stream.generate
创建的流如何以相同的方式停止?有限流不是通过stream.generate创建的
实现流的标准方法是实现流,有时使用。在这两种情况下,实现都有一种报告结束的方法,例如当拆分器.tryAdvance
返回false
或其forEachRemaining
方法刚刚返回时,或者在迭代器
源的情况下,当hasNext()
返回false
时
拆分器甚至可以在处理开始之前报告预期的元素数
通过Stream
接口内的一个工厂方法创建的流,如Stream.generate
,也可以通过拆分器或使用流实现的内部功能来实现,但不管它们是如何实现的,您无法使用此实现来更改它们的行为,因此使此类流有限的唯一方法是将limit
操作链接到流
如果要创建一个非空的有限流,而该流不受数组或集合的支持,并且现有的流源都不适合,则必须实现自己的拆分器
和。如上所述,您可以使用现有的方法从迭代器
中创建拆分器
,但是您应该抵制仅仅因为熟悉而使用迭代器
的诱惑。拆分器不难实现:
/** like {@code Stream.generate}, but with an intrinsic limit */
static <T> Stream<T> generate(Supplier<T> s, long count) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) {
long remaining=count;
public boolean tryAdvance(Consumer<? super T> action) {
if(remaining<=0) return false;
remaining--;
action.accept(s.get());
return true;
}
}, false);
}
我已经为此创建了一个通用的解决方法
public class GuardedSpliterator<T> implements Spliterator<T> {
final Supplier<? extends T> generator;
final Predicate<T> termination;
final boolean inclusive;
public GuardedSpliterator(Supplier<? extends T> generator, Predicate<T> termination, boolean inclusive) {
this.generator = generator;
this.termination = termination;
this.inclusive = inclusive;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
T next = generator.get();
boolean end = termination.test(next);
if (inclusive || !end) {
action.accept(next);
}
return !end;
}
@Override
public Spliterator<T> trySplit() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public long estimateSize() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int characteristics() {
return Spliterator.ORDERED;
}
}
公共类GuardedSpliterator实现Spliterator{
最终供应商为什么要避免使用迭代器来定义拆分器?我刚刚看到BufferedReader.lines()使用这种方法来创建他的有限流。BufferedReader.lines()是一个很好的例子。请看next()的实现
以及它们如何在调用之间保持状态。相比之下,拆分器是直接的,只需要一个方法:tryAdvance(Consumer@WillD:使用Stream.parallel()时,它可以正常工作,这是安全的
。拆分器本身不需要是线程安全的,因为流不会同时访问它。可以通过添加专用的trySplit
实现来提高并行性能,但是请注意,只需使用IntStream.range(0,count).mapToObj即可获得相同的结果(i->supplier.get())
。此答案的代码更像是解决更专业任务的模板。当存在定义良好的回退值时,没有理由通过抛出UnsupportedOperationException来实现接口方法,即estimateSize()
应返回Long.MAX_值
当不支持拆分时,“未知大小”和trySplit()
应该返回null
。但是当您扩展Spliterators.AbstractSpliterator
时,您甚至可以获得拆分支持,而无需实现这些方法。
public class GuardedSpliterator<T> implements Spliterator<T> {
final Supplier<? extends T> generator;
final Predicate<T> termination;
final boolean inclusive;
public GuardedSpliterator(Supplier<? extends T> generator, Predicate<T> termination, boolean inclusive) {
this.generator = generator;
this.termination = termination;
this.inclusive = inclusive;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
T next = generator.get();
boolean end = termination.test(next);
if (inclusive || !end) {
action.accept(next);
}
return !end;
}
@Override
public Spliterator<T> trySplit() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public long estimateSize() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int characteristics() {
return Spliterator.ORDERED;
}
}
GuardedSpliterator<Integer> source = new GuardedSpliterator<>(
() -> rnd.nextInt(),
(i) -> i > 10,
true
);
Stream<Integer> ints = StreamSupport.stream(source, false);
ints.forEach(i -> System.out.println(i));