【问题标题】:takeWhile, dropWhile laziness java9takeWhile, dropWhile 懒惰 java9
【发布时间】:2017-05-26 20:54:08
【问题描述】:

在 scala 中,这些方法工作正常,但在 java9 dropWhile 我认为工作方式不同。

这是 takeWhile 的示例

Stream.of("a", "b", "c", "de", "f", "g", "h")
                .peek(System.out::println)
                .takeWhile(s -> s.length() <= 1)
                .collect(Collectors.toList());

输出很好: a, b, c, de, [a, b, c] 它不处理“de”之后的元素,所以它按预期工作

但 dropWhile 的工作方式与我预期的不同:

Stream.of("a", "b", "c", "de", "f", "g", "h")
                .peek(s -> System.out.print(s + ", "))
                .dropWhile(s -> s.length() <= 1)
                .collect(Collectors.toList());

输出是: a, b, c, de, f, g, h, [de, f, g, h]

所以它不会在“de”元素之后停止,它正在处理整个集合。

为什么要处理整个集合?我知道,它需要获取所有元素并将其收集到列表中,但它不应该在“de”元素之后停止处理吗?

【问题讨论】:

    标签: java scala functional-programming java-stream java-9


    【解决方案1】:

    似乎对peek 的工作原理存在根本性的误解。它与下一个随后的链接操作无关,例如dropWhile,而是与它后面的整个 Stream 管道相关联。并且不区分“处理元素”和“取所有元素”。

    那么简单的代码

    Stream.of("a", "b", "c", "de", "f", "g", "h")
          .peek(System.out::println)
          .collect(Collectors.toList());
    

    “获取所有元素”,但在它们从 Stream 源传递到 Collector 时打印它们。

    在您的示例中,将元素传递给dropWhile 的谓词还是直接传递给Collector 都没有区别,在任何一种情况下,都将通过放置在peek 操作来报告>两者

    如果你使用

    Stream.of("a", "b", "c", "de", "f", "g", "h")
          .dropWhile(s -> {
              System.out.println("dropWhile: "+s);
              return s.length() <= 1;
          })
          .peek(s -> System.out.println("collecting "+s))
          .collect(Collectors.toList());
    

    相反,它会打印

    dropWhile: a
    dropWhile: b
    dropWhile: c
    dropWhile: de
    collecting de
    collecting f
    collecting g
    collecting h
    

    显示dropWhile 的谓词的评估如何在第一个未接受的元素之后停止,而向Collector 的转移从该元素开始。

    这与takeWhile 不同,其中谓词评估和收集器都停止消费元素,因此没有剩余的消费者,整个 Stream 管道可以停止迭代源。 p>

    【讨论】:

      【解决方案2】:

      这是预期的行为,与它在 scala 中的工作方式相同,dropWhile 处理整个流。

      dropWhile 是 takeWhile 的反面。当条件变为假时,takeWhile 停止处理。 dropWhile 处理整个流,但仅在条件为真时不传递任何元素。一旦条件变为假,dropWhile 就会传递所有剩余的元素,而不管条件是真还是假。

      【讨论】:

        【解决方案3】:

        我可能会在这里陈述显而易见的事情,但这对我来说很有意义,因此也可能对您有所帮助。

        takeWhile 获取前 n 个元素,直到 Predicate 被命中(它返回 true)。因此,假设传入流中有 7 个元素,并且 takeWhile 将为前 3 个返回 true,那么生成的流将包含 3 个元素。

        你可以这样想:

        Stream result = Stream.empty();
        while(predicate.apply(streamElement)){ // takeWhile predicate
           result.add(streamElement);
           streamElement = next(); // move to the next element
        }
        return result; 
        

        dropWhile 表示删除元素直到 Predicate 被命中(它返回 true)。那么在删除你的 3 个元素之后,你的结果流会是什么样子?要知道我们需要迭代所有其他元素,这就是 peek 报告其余元素的原因。

        我也喜欢从 Set(而不是 List)中类比 dropWhile。假设这是你的集合:

         Set<String> mySet = Set.of("A", "B", "CC", "D");
        

        你会在这里用相同的谓词做一个 dropWhile (因为它不是一个 List 并且你没有遇到顺序);在 dropWhile 操作之后,除非您迭代所有剩余的元素,否则无法知道结果。

        【讨论】:

          【解决方案4】:

          TL;DR

          始终查看整个流,尤其是终端操作以判断管道的行为方式。 collect 对打印元素的影响与 takeWhiledropWhile 一样大。

          详细说明

          我不确定您是否不同意输出或结果列表。在任何情况下,处理流的不是takeWhile 也不是dropWhile,而是终端操作,在这种情况下是collect。它的任务是收集流中的所有元素,因此通过它“拉”元素,直到流报告不包含更多元素。

          一旦条件第一次变为falsetakeWhile 就是这种情况。在它报告没有更多元素保留后,collect 停止拉取元素,因此输入流的处理停止,来自peek 的消息也停止。

          不过,dropWhile 不同。在对它的第一个请求时,它通过输入流“快进”直到条件第一次变为false(这使得peek打印a, b, c, de。它看到的任何操作之后的第一个元素是@987654334 @. 从那时起,collect 继续从流中拉取元素,直到它被报告为空,一旦输入流以 h 结束时就是这种情况,这会导致 peek 的其余输出。

          用下面的方法试试,结果应该是a, b, c, de [de]。 (我很确定,我什至没有尝试过。;))

          Stream.of("a", "b", "c", "de", "f", "g", "h")
                          .peek(s -> System.out.print(s + ", "))
                          .dropWhile(s -> s.length() <= 1)
                          .findFirst();
          

          【讨论】:

            猜你喜欢
            • 2016-12-14
            • 2014-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-09
            • 2012-06-02
            • 2015-09-06
            • 1970-01-01
            相关资源
            最近更新 更多