我想知道我何时使用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
创建的流如何以相同的方式停止?
答案 0 :(得分:12)
有限流不是通过Stream.generate
创建的。
实现流的标准方法是实现Spliterator
,有时使用the Iterator
detour。在任何一种情况下,实现都有一种报告结束的方法,例如当Spliterator.tryAdvance
返回false
或其forEachRemaining
方法时返回,或者Iterator
来源,当hasNext()
返回false
时。
Spliterator
甚至可以在处理开始之前报告预期的元素数量。
通过Stream
界面中的一个工厂方法创建的流,如Stream.generate
,也可以通过Spliterator
或使用流实现的内部功能实现,但是不管它们是如何实现的,你都不会在这个实现上改变它们的行为,所以使这样的流有限的唯一方法是将limit
操作链接到流。
如果要创建一个没有数组或集合支持的非空有限流,并且没有任何现有流源适合,则必须实现自己的Spliterator
和create 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));