溪流如何停止?

时间:2016-02-05 14:41:48

标签: java-8 java-stream infinite

我想知道我何时使用Stream.generate创建了自己的无限流,标准库中的Streams如何停止...

例如,当您有一个包含记录的列表时:

List<Record> records = getListWithRecords();
records.stream().forEach(/* do something */);

流不会无限并且永远在运行,但是当遍历列表中的所有项目时它将停止。但是这有什么作用呢?相同的功能适用于Files.lines(path)创建的流(来源:http://www.mkyong.com/java8/java-8-stream-read-a-file-line-by-line/)。

第二个问题,用Stream.generate创建的流如何以相同的方式停止?

2 个答案:

答案 0 :(得分:12)

有限流不是通过Stream.generate创建的。

实现流的标准方法是实现Spliterator,有时使用the Iterator detour。在任何一种情况下,实现都有一种报告结束的方法,例如当Spliterator.tryAdvance返回false或其forEachRemaining方法时返回,或者Iterator来源,当hasNext()返回false时。

Spliterator甚至可以在处理开始之前报告预期的元素数量。

通过Stream界面中的一个工厂方法创建的流,如Stream.generate,也可以通过Spliterator或使用流实现的内部功能实现,但是不管它们是如何实现的,你都不会在这个实现上改变它们的行为,所以使这样的流有限的唯一方法是将limit操作链接到流。

如果要创建一个没有数组或集合支持的非空有限流,并且没有任何现有流源适合,则必须实现自己的Spliteratorcreate a stream out of it。如上所述,您可以使用现有方法从Spliterator中创建Iterator,但您应该抵制使用Iterator的诱惑,因为它很熟悉。 Spliterator并不难实现:

/** 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);
}

从这个起点开始,您可以为default接口的Spliterator方法添加覆盖,加权开发费用和潜在的性能改进,例如。

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

        /** May improve the performance of most non-short-circuiting operations */
        @Override
        public void forEachRemaining(Consumer<? super T> action) {
            long toGo=remaining;
            remaining=0;
            for(; toGo>0; toGo--) action.accept(s.get());
        }
    }, false);
}

答案 1 :(得分:0)

我为此

创建了一个通用的解决方法
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));