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
- Optimisation issues when the iteration function is expensive
- 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-8072727Like this:
Like Loading...
Published by lukaseder
I made jOOQ
View all posts by lukaseder