Is there a Java 8 stream operation that limits a (potentially infinite) Stream
until the first element fails to match a predicate?
In Java 9 we can use takeWhile
as in the example below to print all the numbers less than 10.
IntStream .iterate(1, n -> n + 1) .takeWhile(n -> n < 10) .forEach(System.out::println);
As there is no such operation in Java 8, what's the best way of implementing it in a general way?
IntStream limit() method in JavaThe limit() method of the IntStream class is used to return a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length. Here, maxSize is the parameter. Here, the maxSize parameter is the count of elements the stream is limited to.
Stream limit(n) is used to retrieve a number of elements from the Stream while the count must not be greater than n. The limit() method returns a new Stream consisting of the elements of the given stream, truncated to be no longer than maxSize in length.
The limit method of the Stream class introduced in Java 8 allows the developer to limit the number of elements that will be extracted from a stream. The limit method is useful in those applications where the user wishes to process only the initial elements that occur in the stream.
No storage. Streams don't have storage for values; they carry values from a source (which could be a data structure, a generating function, an I/O channel, etc) through a pipeline of computational steps.
Operations takeWhile
and dropWhile
have been added to JDK 9. Your example code
IntStream .iterate(1, n -> n + 1) .takeWhile(n -> n < 10) .forEach(System.out::println);
will behave exactly as you expect it to when compiled and run under JDK 9.
JDK 9 has been released. It is available for download here: JDK 9 Releases.
Such an operation ought to be possible with a Java 8 Stream
, but it can't necessarily be done efficiently -- for example, you can't necessarily parallelize such an operation, as you have to look at elements in order.
The API doesn't provide an easy way to do it, but what's probably the simplest way is to take Stream.iterator()
, wrap the Iterator
to have a "take-while" implementation, and then go back to a Spliterator
and then a Stream
. Or -- maybe -- wrap the Spliterator
, though it can't really be split anymore in this implementation.
Here's an untested implementation of takeWhile
on a Spliterator
:
static <T> Spliterator<T> takeWhile( Spliterator<T> splitr, Predicate<? super T> predicate) { return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) { boolean stillGoing = true; @Override public boolean tryAdvance(Consumer<? super T> consumer) { if (stillGoing) { boolean hadNext = splitr.tryAdvance(elem -> { if (predicate.test(elem)) { consumer.accept(elem); } else { stillGoing = false; } }); return hadNext && stillGoing; } return false; } }; } static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) { return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With