Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit a stream by a predicate

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?

like image 783
MForster Avatar asked Dec 23 '13 15:12

MForster


People also ask

How do you limit a stream in Java?

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.

What is limit stream?

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.

What is the purpose of the limit () method in Java 8?

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.

Do streams have limited storage?

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.


2 Answers

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.

like image 163
Stuart Marks Avatar answered Oct 14 '22 11:10

Stuart Marks


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); } 
like image 28
Louis Wasserman Avatar answered Oct 14 '22 10:10

Louis Wasserman