在无限流上调用.map()?

时间:2018-06-25 16:52:44

标签: java lambda functional-programming java-stream functional-interface

根据Java文档SE 8 Stream.map()执行以下

  

返回一个流,该流包含将给定功能应用于此流的元素的结果。

但是,我正在阅读的关于联网的书(学习Java语言的网络编程,Richard M. Reese)在回显服务器中大致实现了以下代码段。

Supplier<String> inputLine = () -> {
    try {
        return br.readLine();
    } catch(IOException e) {
        e.printStackTrace();
        return null;
    }
};

Stream.generate(inputLine).map((msg) -> {
    System.out.println("Recieved: " + (msg == null ? "end of stream" : msg));
    out.println("echo: " + msg);
    return msg;
}).allMatch((msg) -> msg != null);

这应该是完成将用户输入打印到套接字输入流的功能方法。它按预期工作,但我不太了解如何使用。是因为map知道流是无限的,所以当新的流令牌可用时,它会延迟执行吗?似乎将一些东西添加到当前正在被map迭代的集合中有点黑魔法。有人可以帮我了解幕后发生的事情吗?


在这里,我重申了这一点,以避免混淆地图的使用。我相信作者正在尝试避免无限循环,因为您不能脱离forEach。

Stream.generate(inputLine).allMatch((msg) -> {
        boolean alive = msg != null;
        System.out.println("Recieved: " + (alive ? msg : "end of stream"));
        out.println("echo: " + msg);

        return alive;
});

3 个答案:

答案 0 :(得分:3)

流是惰性的。可以将他们视为链条中彼此相通的工人。懒惰的事实是,如果前面的工作人员要求他们,他们只会向后面的工作人员要求下一个铲斗。

因此,最好将其视为allMatch-作为最终动作,因此急于-向map流询问下一项,而map流询问{{ 1}}流向下一个项目,而generate流向其供应商,并在到达时立即提供该项目。

generate停止索要物品时,它停止。当知道答案时,它就会这样做。此流中的所有项目都不为空吗? allMatch收到一个为空的项目后,就知道答案为allMatch,并且将结束并且不再要求其他项目。因为流是无限的,否则它不会停止。

因此,有两个因素导致此方法以其工作方式起作用:一个是false急切地询问下一个项目(只要前一个不为空),而allMatch为了提供下一个项目,可能需要等待等待用户发送更多输入的供应商。

但是应该说generate在这里不应该被使用。 map中不应有副作用-应该将其用于将一种类型的项目映射到另一种类型的项目。我认为这个例子仅用作学习辅助工具。一种更简单明了的方法是使用BufferedReader's method lines(),它为您提供了map有限的来自缓冲读取器的行。

答案 1 :(得分:2)

是-除非您对Stream执行终端操作(最终操作),否则Stream会延迟设置。或更简单:

只要流上的操作返回另一个Stream,就没有终端操作,并且一直保持链接状态,直到返回除Stream以外的任何东西,包括void。

这是有道理的,因为要能够返回Stream以外的任何内容,就需要评估流中较早的操作才能真正提供数据。

在这种情况下,根据文档,allMatch返回一个boolean,因此需要最终执行流以计算该布尔值。这也是您提供Predicate来限制生成的Stream的地方。

还要注意,在文档中指出:

  

这是short-circuiting terminal operation

单击该链接可获得有关这些终端操作的更多信息,但是终端操作基本上意味着它将实际执行该操作。此外,无限流的限制是该方法的“短路”方面。

答案 2 :(得分:1)

这是文档中两个最相关的句子。您提供的代码段是这些协同工作的完美示例:

  • Stream::generate(Supplier<T> s)说它返回:

      

    返回无限顺序无序流,其中每个元素由提供的Supplier生成。

  • 流包文档的
  • 3rd dot

      

    寻求懒惰。许多流操作(例如过滤,映射或重复删除)可以延迟实施,从而暴露出进行优化的机会。例如,“使用三个连续的元音查找第一个字符串”不需要检查所有输入字符串。流操作分为中间(流产生)操作和终端(产生值或副作用)操作。中间操作总是很懒。

在快捷方式中,此生成的流将等待其他元素,直到达到终端操作为止。只要在提供的Supplier<T>中执行,流管道就会继续。

例如,如果您提供以下Supplier,则执行将没有机会停止,并且将无限期地继续执行:

Supplier<String> inputLine = () -> {
    return "Hello world";
};