首页 > 解决方案 > 在无限流上调用 .map()?

问题描述

根据 SE 8 Stream.map() 的 Javadocs,执行以下操作

返回由将给定函数应用于此流的元素的结果组成的流。

但是,我正在阅读的一本关于网络的书(Learning Network Programming with 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;
});

标签: javalambdafunctional-programmingjava-streamfunctional-interface

解决方案


流是懒惰的。将它们想象成链条中的工人,它们将桶相互传递。懒惰在于,如果他们前面的工人向他们要下一个桶,他们只会向他们后面的工人要下一个桶。

因此,最好将其视为allMatch- 作为最终操作,因此急切 - 向map流请求下一个项目,map流向流请求generate下一个项目,generate流向其供应商,并尽快提供该项目当它到达时。

allMatch停止索取物品时它会停止。当它知道答案时,它就会这样做。此流中的所有项目都不为空吗?一旦allMatch收到一个为空的项目,它就知道答案是false,并且将完成并且不再要求任何更多项目。因为流是无限的,否则它不会停止。

所以你有两个因素导致它以它的工作方式工作 - 一个是allMatch急切地要求下一个项目(只要前一个不为空),以及generate流 - 为了提供下一个项目 - 可能需要等待等待用户发送更多输入的供应商。

但应该说不map应该在这里使用。不应该有副作用map- 它应该用于将一种类型的项目映射到另一种类型的项目。我认为此示例仅用作学习辅助工具。更简单直接的方法是使用BufferedReader' 方法lines()Stream,该方法为您提供来自缓冲阅读器的有限行。


推荐阅读