我的理解是它需要检查hasNext
,然后如果它是true
,则调用下一个方法来获取迭代器中的元素。
问题:
它在检索元素后调用hasNext
,在我的例子中,使用Next检索最后一个元素并检查hasNext
,然后返回false
并结束循环。所以我处理N-1
元素而不是N
元素。
或者,我使用了下面的代码。
Stream.generate(() -> j)
.takeWhile(SlidingWindow::hasNextTimePeriod)
.map(slider -> slider.next())
复制代码:
@Slf4j
class Java9StreamTest {
@Test
void testStream() {
final SlidingWindows slidingWindows = new SlidingWindows();
Stream.iterate(slidingWindows.next(), j -> slidingWindows.hasNext(), j -> slidingWindows.next())
.forEach(value -> log.info("{}", value));
}
}
class SlidingWindows {
private final List<String> list = List.of("A", "B", "C");
private int count = 0;
public boolean hasNext() {
return count < list.size();
}
public String next() {
final String result = list.get(count);
count = count + 1;
return result;
}
}
输出:
20:20:03.485 [main] INFO mohan.stream.Java9StreamTest - A
20:20:03.489 [main] INFO mohan.stream.Java9StreamTest - B
缺少最后一个元素C
它的工作原理类似于for循环,在Java API中,在文档中说hasNext和next是误导性的。
什么相当于Streams中的以下代码?
while(slidingWindows.hasNext()) {
final String next = slidingWindows.next();
log.info("{}", next);
}
根据留档Stream.iterate()
在引擎盖下利用for
循环:
流动iterate应该生成与相应for循环生成的元素序列相同的元素序列:
for (T index=seed; hasNext.test(index); index = next.apply(index)) {
...
}
如果hasNext谓词不保留种子值,则生成的序列可能为空。否则,第一个元素将是提供的种子值,下一个元素(如果存在)将是对种子值应用next函数的结果,以此类推,直到hasNext谓词指示流应该终止。
所以基本上iterate()
种子的第一个参数对应于for
循环的初始化表达式,第二个参数(谓词)充当终止表达式,第三个参数(一元操作符)扮演增量表达式的角色(见)。
让我们看两个示例:
Iterator<String> iter = List.of("A", "B", "C").iterator();
Stream.iterate(iter.next(), str -> iter.hasNext(), str -> iter.next())
.forEach(System.out::println);
其作用与:
Stream.generate(() -> "dummy")
.map(str -> iter.next()) // requesting the element
.peek(str -> System.out.println(str + " <- invoking next()"))
.takeWhile(str -> {
boolean result = iter.hasNext(); // checking whether the next element exists
System.out.println(result + " <- invoking hasNext()");
return result;
})
.forEach(str -> System.out.println(str + " <- has reached the terminal operation\n"));
第二流的输出:
A <- invoking next()
true <- invoking hasNext()
A <- has reached the terminal operation
B <- invoking next()
true <- invoking hasNext()
B <- has reached the terminal operation
C <- invoking next()
false <- invoking hasNext()
让我们探索所有操作:
>
种子iter.next()
将检索第一个元素A
(在第二个流中,它将由map
操作完成)。然后谓词iter.next()
将被触发。
然后将检索第二个元素B,谓词将发挥作用。
然后第三个元素C
将跟随,谓词iter.next()
将返回false
,因为源代码已经耗尽。
结论:在这两种情况下,最后一个元素C
在谓词执行之前从源中获取,并且由于源已经为空,谓词将被评估为false
并且这个元素将被扔掉。
要成功地从源中检索所有元素,您需要交换映射和takeWhile,即首先检查是否有下一个元素,然后才请求下一个元素:
Iterator<String> iter = List.of("A", "B", "C").iterator();
Stream.generate(() -> "dummy")
.takeWhile(str -> iter.hasNext())
.map(str -> iter.next())
.forEach(System.out::println);
输出:
A
B
C
此修复仅适用于组合takeWhile,但我们无法更改迭代()的行为。
正如@Slow在评论中所提到的,如果您让类实现迭代器接口,那么您可以使用流支持实用程序类,该实用程序类将负责将所有元素转换为流,正如这里所述:为什么Iterable不提供stream()和parallelStream()方法?
请注意,它还需要将迭代器转换为拆分器的实例,这可以通过使用拆分器工具类的静态方法来完成。
StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED),
false) // a flag that denotes whether the stream should be parallel
.forEach(System.out::println);