从Java 8流中获取下一个项目

我想从Java 8 Stream检索并删除下一个项目,而不会关闭此Stream

 Stream integerStream = Stream.iterate( 0, x -> new Integer(x + 1) ); Integer zero = integerStream.getNext(); // 0 Integer one = integerStream.getNext(); // 1 ... 

这可能吗?

是的,有一种方法可以做到这一点,但有一些限制。

 Stream infiniteStream = Stream.iterate( 0, x -> new Integer(x + 1) ); Iterator iter = infiniteStream.iterator(); Integer zero = iter.next(); Integer one = iter.next(); 

或者,

 Stream infiniteStream = Stream.iterate( 0, x -> new Integer(x + 1) ); Spliterator spliterator = infiniteStream.spliterator(); spliterator.tryAdvance(i -> System.out.println(i)); // zero spliterator.tryAdvance(i -> System.out.println(i)); // one 

给定一个Stream ,可以从中获取IteratorSpliterator ,或查询它是否是并行流等。这些是在BaseStream接口上定义的,这是StreamBaseStream接口,这使得它们很容易被遗漏。

在这种情况下,我们知道流是无限的,因此不需要调用Iterator的hasNext()方法或检查Spliterator的tryAdvance()的返回值

限制是Streamiterator()spliterator()方法都是终端操作 ,这意味着在调用它们之后,返回的Iterator或Spliterator可以独占访问Stream表示的值。 不允许对流进行进一步操作(例如filtermap等),并且将遇到IllegalStateException

如果你想剥离前几个元素然后恢复流处理,你可以将分裂器重新转换为如下所示的流:

 Stream stream2 = StreamSupport.stream(spliterator, false); 

对于某些事情,这可能会很好,但我不确定我会推荐这种技术。 我认为它会在生成下一个元素的路径中添加一些额外的对象,从而增加额外的方法调用。

编辑评论(与您的问题无关):

  • 不要使用new Integer(val) 。 而是使用Integer.valueOf(val) ,如果它可用,它将重用盒装整数,对于-128到127范围内的值通常是正确的。
  • 您可以使用IntStream而不是Stream来完全避免装箱开销。 它没有完整的流操作,但它确实有iterate() ,它接受一个对原始int值进行操作的函数。

根据Stuart的回答和Iterator-to-Stream转换 ,我提出了以下快速而肮脏的包装器类。 它没有经过测试,并且它不是线程安全的,但它为我提供了我目前需要的东西 – 删除和使用单个项目,同时保持流“开放”。

PeelingStream提供了一个方法T getNext()来屏蔽someWrappedStream.iterator()终端流操作语义:

 public class PeelingStream implements Stream { private Stream wrapped; public PeelingStream(Stream toBeWrapped) { this.wrapped = toBeWrapped; } public T getNext() { Iterator iterator = wrapped.iterator(); T next = iterator.next(); Iterable remainingIterable = () -> iterator; wrapped = StreamSupport.stream(remainingIterable.spliterator(), false); return next; } ///////////////////// from here, only plain delegate methods public Iterator iterator() { return wrapped.iterator(); } public Spliterator spliterator() { return wrapped.spliterator(); } public boolean isParallel() { return wrapped.isParallel(); } public Stream sequential() { return wrapped.sequential(); } public Stream parallel() { return wrapped.parallel(); } public Stream unordered() { return wrapped.unordered(); } public Stream onClose(Runnable closeHandler) { return wrapped.onClose(closeHandler); } public void close() { wrapped.close(); } public Stream filter(Predicate predicate) { return wrapped.filter(predicate); } public  Stream map(Function mapper) { return wrapped.map(mapper); } public IntStream mapToInt(ToIntFunction mapper) { return wrapped.mapToInt(mapper); } public LongStream mapToLong(ToLongFunction mapper) { return wrapped.mapToLong(mapper); } public DoubleStream mapToDouble(ToDoubleFunction mapper) { return wrapped.mapToDouble(mapper); } public  Stream flatMap( Function> mapper) { return wrapped.flatMap(mapper); } public IntStream flatMapToInt( Function mapper) { return wrapped.flatMapToInt(mapper); } public LongStream flatMapToLong( Function mapper) { return wrapped.flatMapToLong(mapper); } public DoubleStream flatMapToDouble( Function mapper) { return wrapped.flatMapToDouble(mapper); } public Stream distinct() { return wrapped.distinct(); } public Stream sorted() { return wrapped.sorted(); } public Stream sorted(Comparator comparator) { return wrapped.sorted(comparator); } public Stream peek(Consumer action) { return wrapped.peek(action); } public Stream limit(long maxSize) { return wrapped.limit(maxSize); } public Stream skip(long n) { return wrapped.skip(n); } public void forEach(Consumer action) { wrapped.forEach(action); } public void forEachOrdered(Consumer action) { wrapped.forEachOrdered(action); } public Object[] toArray() { return wrapped.toArray(); } public  A[] toArray(IntFunction generator) { return wrapped.toArray(generator); } public T reduce(T identity, BinaryOperator accumulator) { return wrapped.reduce(identity, accumulator); } public Optional reduce(BinaryOperator accumulator) { return wrapped.reduce(accumulator); } public  U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { return wrapped.reduce(identity, accumulator, combiner); } public  R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { return wrapped.collect(supplier, accumulator, combiner); } public  R collect(Collector collector) { return wrapped.collect(collector); } public Optional min(Comparator comparator) { return wrapped.min(comparator); } public Optional max(Comparator comparator) { return wrapped.max(comparator); } public long count() { return wrapped.count(); } public boolean anyMatch(Predicate predicate) { return wrapped.anyMatch(predicate); } public boolean allMatch(Predicate predicate) { return wrapped.allMatch(predicate); } public boolean noneMatch(Predicate predicate) { return wrapped.noneMatch(predicate); } public Optional findFirst() { return wrapped.findFirst(); } public Optional findAny() { return wrapped.findAny(); } } 

一个小测试:

 @Test public void testPeelingOffItemsFromStream() { Stream infiniteStream = Stream.iterate(0, x -> x + 1); PeelingStream peelingInfiniteStream = new PeelingStream<>(infiniteStream); Integer one = peelingInfiniteStream.getNext(); assertThat(one, equalTo(0)); Integer two = peelingInfiniteStream.getNext(); assertThat(two, equalTo(1)); Stream limitedStream = peelingInfiniteStream.limit(3); // 2 3 4 int sumOf234 = limitedStream.mapToInt(x -> x.intValue()).sum(); assertThat(sumOf234, equalTo(2 + 3 + 4)); }