【问题标题】:Closing mapped streams - what's the idea?关闭映射流 - 这是什么想法?
【发布时间】:2016-04-22 07:31:20
【问题描述】:

众所周知,Javadoc 提到了Stream 接口:

Streams 有一个 BaseStream.close() 方法并实现 AutoCloseable, 但几乎所有流实例实际上都不需要关闭 使用后。 一般来说,只有源是 IO 通道的流(例如 因为 Files.lines(Path, Charset)) 返回的那些需要关闭。 大多数流由集合、数组或生成器支持 功能,不需要特殊的资源管理。 (如果一个流 确实需要关闭,它可以声明为一个资源 try-with-resources 语句。)

好的,但是同时这个接口里面还有flatMapToInt这样的方法:

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);

Javadoc 规范规定:

每个映射流在其内容被放入后关闭 这个流。

所以,我不明白:如果IntStream 的源中没有设计为具有 IO 通道,为什么它会在此方法中关闭?

例如,ReferencePipeline 实现是这样的:

try (IntStream result = mapper.apply(u)) {     
   if (result != null)
       result.sequential().forEach(downstreamAsInt);
}

更一般的问题可能是:我们是否应该关心关闭像IntStream(或其后代)这样的流?如果不是,那为什么flatMapTo* 关心?

编辑 @Tunaki 为我提供了一个非常有趣的email link。但这一切都是关于flatMap,我同意,在一般情况下我们应该关闭流。但我的问题是关于特殊情况:flatMapToIntflatMapToLong 等等,我认为没有必要关闭流。

EDIT-2 @BrianGoetz 在这里上诉,因为这是他引用的电子邮件,因此他在主题中:)

【问题讨论】:

  • flatMapToIntflatMap 区别对待是没有意义的。 IntStream 也可以持有资源;仅仅因为尾部出现的是int,并不意味着源不是以非内存资源开始的。例如,假设有一个 flatMapToInt,其中每个元素都被平面映射到 Files.lines().mapToInt(String::length)?

标签: java java-8 java-stream


【解决方案1】:

关于资源处理的一般规则是whoever is responsible for closing a resource is the one that opened itflatMap 操作是 Stream API 中唯一打开 Stream 的操作,因此它是唯一会关闭它的操作。

引用this mail,Brian Goetz 说:

总而言之,flatMap() 是唯一在内部关闭 完成后流式传输,并且有充分的理由 - 这是唯一的情况 流由操作本身有效地打开,因此 也应该被操作关闭。假设任何其他流 由调用者打开,因此应该由调用者关闭。

给出的例子如下。考虑

try (Stream<Path> paths = Files.walk(dir)) {
    Stream<String> stream = paths.flatMap(p ->  {
        try {
            return Files.lines(p);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    });
}

方法引用Files::lines 返回文件行的Stream&lt;String&gt;。当平面映射操作结束时,预期用于读取文件的打开资源关闭。问题是:被什么封闭?好吧,由 flatMap 本身关闭,因为它是首先打开 Stream 的操作。

Files.lines 返回一个带有预注册关闭处理程序的流,该处理程序关闭底层BufferedReader。 flatMap 操作完成后,将调用此关闭处理程序并正确释放资源。


这个想法被反向移植到flatMapTo*操作的原因是一样的:遵守上面的规则,一个进程分配的每个资源都应该被那个进程关闭。

只是为了表明您可以构建一个 IntStream 来关闭底层资源,请考虑以下 Stream 管道,其中每个路径不是平面映射到其行,而是映射到每行中的字符数。

try (Stream<Path> paths = Files.walk(dir)) {
    IntStream stream = paths.flatMapToInt(p ->  {
        try {
            return Files.lines(p).mapToInt(String::length);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    });
}

【讨论】:

  • 假定任何其他流由调用者打开,因此应该由调用者关闭 - 这意味着我们应该对所有流进行操作(比如一些list.stream() ) 与try-with-resourse 构造??
  • @Andremoniy 对于大多数 Streams 来说,这将是一个空操作。唯一相关的是处理应该关闭的底层资源。
  • 好的,flatMap 我同意,但 Int/LongStreams 的想法仍然存在
  • @Andremoniy 我看到 2 个原因:一致性和规则“你应该关闭你打开的东西”
  • @Andremoniy 恐怕我无法回答这个问题(只能猜测),因为我不是 JDK 开发人员;)。
猜你喜欢
  • 2014-08-22
  • 2020-08-16
  • 1970-01-01
  • 2017-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
相关资源
最近更新 更多