一interesting question by Tagir Valeev on Stack Overflow最近引起了我的注意。简而言之(阅读问题的细节),下面的代码是有效的:
public static Stream<Long> longs() {
return Stream.iterate(1L, i ->
1L + longs().skip(i - 1L)
.findFirst()
.get());
}
longs().limit(5).forEach(System.out::println);
打印:
以下类似的代码不起作用:
public static LongStream longs() {
return LongStream.iterate(1L, i ->
1L + longs().skip(i - 1L)
.findFirst()
.getAsLong());
}
引起StackOverflowError
。
当然,这种递归迭代不是最佳的。它不是在Java 8之前,也肯定不是在新的应用编程接口中。但是有人可能认为它至少应该起作用,对吗?它不起作用的原因是因为两者之间的细微实现差异iterate()
Java 8中的方法。而引用类型流的Iterator
首先返回seed
然后通过对前一个值应用迭代函数来继续迭代:
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);
}
};
事实并非如此LongStream.iterate()
版本(和其他原始流):
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;
}
};
迭代函数已经预先提取了一个值。这通常不是问题,但会导致:
作为一种变通方法,最好在基元类型流中简单地避免使用此方法的递归。幸运的是,JDK 9的修复已经在进行中(作为特性增强的副作用):
https://bugs.openjdk.java.net/browse/JDK-8072727