Oh No, I Forgot Stream::iterate!

There I go babbling about new stream methods and then I forget one: a Stream::iterate overload that produces a finite stream.

There I go talking about the new things that Java 9 will bring to the stream API and then I forget one: a new overload for iterate. D'oh! I updated that post but to make sure you don't miss it, I also put it into this one.


Stream already has a method iterate. It's a static factory method that takes a seed element of type T and a function from T to T. Together they are used to create a Stream<T> by starting with the seed element and iteratively applying the function to get the next element:

Stream.iterate(1, i -> i + 1)
// output: 1 2 3 4 5 ...

Great! But how can you make it stop? Well, you can't, the stream is infinite.

Or rather you couldn't because this is where the new overload comes in. It has an extra argument in the middle: a predicate that is used to assess each element before it is put into the stream. As soon as the first elements fails the test, the stream ends:

Stream.iterate(1, i -> i <= 3, i -> i + 1)
// output: 1 2 3

As it is used above, it looks more like a traditional for loop than the more succinct but somewhat alien IntStream.rangeClosed(1, 3) (which I still prefer but YMMV). It can also come in handy to turn "iterator-like" data structures into streams, like the ancient Enumeration:

Enumeration<Integer> en = // ...
if (en.hasMoreElements()) {
			el -> en.hasMoreElements(),
			el -> en.nextElement())

You could also use it to manipulate a data structure while you stream over it, like popping elements off a stack. This not generally advisable, though, because the source may end up in a surprising state - you might want to discard it afterwards.


Not True! Turns out neither the Enumeration above nor the Stack mentioned in the link can be streamed like this - at least not fully. The predicate (in our cases el -> en.nextElement() and el -> stack.pop()) is evaluated after an element was taken from the source. This is in line with how the traditional for-loop works but has an unfortunate effect.

After taking the last element from the source but before pushing it into the stream, the predicate is consulted and returns false because there is no element after the last one. The element does hence not appear in the stream, which means the last element is always missing.

Thanks to Piotr for pointing this out!