【问题标题】:Do terminal operations close the stream?终端操作是否关闭流?
【发布时间】:2014-11-18 14:55:01
【问题描述】:

dirPath 包含 200k 个文件。我想一一阅读并做一些处理。以下 sn-p 导致java.nio.file.FileSystemException: dirPath/file-N Too many open files。终端操作forEach() 不应该在移动到下一个之前关闭打开的流(即打开的文件)吗?换句话说,我是否必须为流文件添加 try-with-resources?

Files.list(dirPath)
     .forEach(filePath -> {
              Files.lines(filePath).forEach() { ... }
              });

【问题讨论】:

标签: java-8 java-stream


【解决方案1】:

No forEach 不会关闭流(由Files.listFiles.lines 创建)。它记录在 javadoc 中,例如 Files.list:

返回的流封装了一个 Reader。如果需要及时处理文件系统资源,则应使用 try-with-resources 构造来确保在流操作完成后调用流的 close 方法。

【讨论】:

    【解决方案2】:

    在大多数情况下,嵌套的 forEach 是错误的工具。

    代码

    Files.list(dirPath).forEach(filePath -> Files.lines(filePath).forEach(line -> { ... });
    

    可以并且应该替换为

    Files.list(dirPath).flatMap(filePath -> Files.lines(filePath)).forEach(line -> { ... });
    

    或者好吧,因为在这种情况下并不那么容易:

    Files.list(dirPath).flatMap(filePath -> {
        try { return Files.lines(filePath);}
        catch(IOException ex) { throw new UncheckedIOException(ex); }
    }).forEach(line -> {  });
    

    作为副作用,您可以免费获得以下内容:

    Stream.flatMap(…):

    每个映射的流在其内容放入该流后关闭。

    所以这是首选的解决方案。或者好吧,让它完全正确:

    try(Stream<Path> dirStream = Files.list(dirPath)) {
        dirStream.flatMap(filePath -> {
            try { return Files.lines(filePath);}
            catch(IOException ex) { throw new UncheckedIOException(ex); }
        }).forEach(line -> { });
    }
    

    【讨论】:

    • 我认为您误读了 flatMap 的文档。 flatMap 采用一个函数,该函数本身返回许多流; flatMap 所做的是确保从传递给 flatMap 的函数返回的这些流在这些流的结果被展平到 flatMap 返回的流中时关闭。 flatMap 与 map 应用的流没有什么不同。
    • (或者我猜你在随后的编辑中发现了这一点。:))
    • @BarryKelly 我从来没有说过flatMap 对外部流有影响。单个外部流不是 OP 的问题,而是传递给 flatMap 的函数产生的 200k 流。所以我提供了一个关闭这 200k 流的解决方案。但由于让一个流打开仍然不是一个干净的行为,我在编辑时提供了完整的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 2018-03-23
    • 1970-01-01
    • 2022-12-26
    • 2014-04-05
    • 2020-10-07
    相关资源
    最近更新 更多