众所周知,Javadoc说Stream
接口:
Streams有一个BaseStream.close()方法并实现AutoCloseable, 但几乎所有流实例实际上都不需要关闭 使用后。 通常,只有源为IO通道的流(例如 由Files.lines(Path,Charset)返回的那些将需要关闭。 大多数流都由集合,数组或生成支持 功能,不需要特殊的资源管理。 (如果是一个流 确实需要关闭,它可以被声明为一个资源 尝试使用资源声明。)
好的,但同时在此界面中有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
,我同意在一般情况下我们应该关闭。但我的问题是关于特殊情况:flatMapToInt
,flatMapToLong
等等,我认为没有必要关闭流。
EDIT-2 @BrianGoetz在这里上诉,因为这是他引用的电子邮件,因此他在主题中:)
答案 0 :(得分:15)
关于资源处理的一般规则是whoever is responsible for closing a resource is the one that opened it。 flatMap
操作是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<String>
行。当平面映射操作结束时,预期用于读取文件的打开资源被关闭。问题是:关闭什么?好吧,由flatMap本身关闭,因为它是首先打开Stream的操作。
Files.lines
返回一个Stream,其中包含一个预先注册的关闭处理程序,用于关闭基础BufferedReader
。完成flatMap操作后,将调用此关闭处理程序并正确释放资源。
这个想法被移植到flatMapTo*
操作的原因是相同的:坚持上述规则,即流程分配的每个资源都应该由该流程关闭。
为了表明您可以构建一个可以关闭基础资源的IntStream
,请考虑以下Stream管道,其中每个路径都不是平面映射到其行,而是每行中的字符数。< / p>
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);
}
});
}