提问者:小点点

Java 9流。迭代​(T seed,Predicate<?super T>hasNext,UnaryOperator<T>next)调用后调用的hasNext


我的理解是它需要检查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); 
 }

共1个答案

匿名用户

根据留档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);