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