VOOZH about

URL: https://www.javacodegeeks.com/2016/03/watch-recursion-java-8s-primitivestream-iterate.html

⇱ Watch Out For Recursion in Java 8's [Primitive]Stream.iterate() - Java Code Geeks


An interesting question by Tagir Valeev on Stack Overflow has recently caught my attention. To keep things short (read the question for details), while the following code works:

public static Stream<Long> longs() {
 return Stream.iterate(1L, i ->
 1L + longs().skip(i - 1L)
 .findFirst()
 .get());
}

longs().limit(5).forEach(System.out::println);

printing

1
2
3
4
5

The following, similar code won’t work:

public static LongStream longs() {
 return LongStream.iterate(1L, i ->
 1L + longs().skip(i - 1L)
 .findFirst()
 .getAsLong());
}

Causing a StackOverflowError.

Sure, this kind of recursive iteration is not optimal. It wasn’t prior to Java 8 and it certainly isn’t with the new APIs either. But one might think it should at least work, right? The reason why it doesn’t work is because of a subtle implementation difference between the two iterate() methods in Java 8. While the reference type stream’s Iterator first returns the seed and only then proceeds with iterating by applying the iteration function on the previous value:

final Iterator<T> iterator = new Iterator<T>() {
 @SuppressWarnings("unchecked")
 T t = (T) Streams.NONE;

 @Override
 public boolean hasNext() {
 return true;
 }

 @Override
 public T next() {
 return t = (t == Streams.NONE) ? seed : f.apply(t);
 }
};

This is not the case for the LongStream.iterate() version (and other primitive streams):

final PrimitiveIterator.OfLong iterator = new PrimitiveIterator.OfLong() {
 long t = seed;

 @Override
 public boolean hasNext() {
 return true;
 }

 @Override
 public long nextLong() {
 long v = t;
 t = f.applyAsLong(t);
 return v;
 }
};

The iteration function is already pre-fetched one value in advance. This is usually not a problem, but can lead to

  1. Optimisation issues when the iteration function is expensive
  2. Infinite recursions when the iterator is used recursively

As a workaround, it might be best to simply avoid recursion with this method in primitive type streams. Luckily, a fix in JDK 9 is already on its way (as a side effect for a feature enhancement): https://bugs.openjdk.java.net/browse/JDK-8072727

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

👁 Photo of Lukas Eder
Lukas Eder
March 29th, 2016Last Updated: March 29th, 2016
0 144 1 minute read

Lukas Eder

Lukas is a Java and SQL enthusiast developer. He created the Data Geekery GmbH. He is the creator of jOOQ, a comprehensive SQL library for Java, and he is blogging mostly about these three topics: Java, SQL and jOOQ.
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button
Close
wpDiscuz