Java 8 Streams并尝试使用资源

fan*_*fan 21 java eclipse java-8 java-stream

我认为流API在这里使代码更容易阅读.我发现了一些很烦人的东西.的Stream接口(java.util.stream.Stream)延伸的AutoClosable接口(java.lang.AutoCloseable)

因此,如果要正确关闭流,则必须使用try with resources.

清单1.不太好,溪流没有关闭.

   public void noTryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

   @SuppressWarnings("resource") List<ImageView> collect = photos.stream()
            .map(photo -> new ImageView(new Image(String.valueOf(photo)))).collect(Collectors.<ImageView>toList());
}
Run Code Online (Sandbox Code Playgroud)

清单2.2个imbricated尝试:(

   public void tryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream()) {
        try (Stream<ImageView> map = stream
                .map(photo -> new ImageView(new Image(String.valueOf(photo))))) {
            List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

清单3.当map返回流时,必须关闭stream()map()函数.

    public void tryWithResource2() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream();
         Stream<ImageView> map = stream
                 .map(photo -> new ImageView(new Image(String.valueOf(photo))))) {
        List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
    }
}
Run Code Online (Sandbox Code Playgroud)

我给出的例子没有任何意义.为了这个例子,我用Pathjpg图像替换Integer了.但是不要让你分心这些细节.

使用那些可自动关闭的流的最佳方法是什么.我不得不说我对我展示的3个选项中的任何一个都不满意.你怎么看?还有其他更优雅的解决方案吗?

Stu*_*rks 27

你正在使用@SuppressWarnings("resource")哪个可能会抑制关于未封闭资源的警告.这不是发出的警告之一javac.Web搜索似乎表明,如果AutoCloseable未关闭,Eclipse会发出警告.

根据引入的Java 7规范,这是一个合理的警告AutoCloseable:

在不再需要时必须关闭的资源.

然而,Java的8规格AutoCloseable很轻松删除"必须关闭"的条款.它现在部分地说

一个可以保存资源的对象......直到它被关闭.

对于基类来说,实现AutoCloseable是可能的,并且实际上是常见的,即使并非所有子类或实例都将保留可释放的资源.对于必须完全通用运行的代码,或者当已知AutoCloseable实例需要资源释放时,建议使用try-with-resources结构.但是,当使用支持基于I/O和非I/O的表单的Stream等工具时,在使用非基于I/O的表单时,通常不需要try-with-resources块.

Lambda专家组对此问题进行了广泛讨论; 此消息总结了该决定.除其他事项外,它提到了AutoCloseable规范(上面引用)和BaseStream规范(其他答案引用)的变化.它还提到可能需要针对更改的语义调整Eclipse代码检查器,可能不会无条件地发出AutoCloseable对象的警告.显然这条消息没有传达给Eclipse的人,或者他们还没有改变它.

总之,如果Eclipse警告引导您认为您需要关闭所有AutoCloseable对象,那就不正确了.只AutoCloseable需要关闭某些特定对象.Eclipse需要修复(如果还没有)不要为所有AutoCloseable对象发出警告.


dka*_*zel 12

如果流需要对其自身进行任何清理(通常是I/O),则只需关闭Streams.您的示例使用HashSet,因此不需要关闭它.

来自Stream javadoc:

通常,只有源为IO通道的流(例如Files.lines(Path,Charset)返回的流)才需要关闭.大多数流都由集合,数组或生成函数支持,不需要特殊的资源管理.

所以在你的例子中,这应该没有问题

List<ImageView> collect = photos.stream()
                       .map(photo -> ...)
                       .collect(toList());
Run Code Online (Sandbox Code Playgroud)

编辑

即使您需要清理资源,您也应该只使用一次try-with-resource.让我们假装您正在读取一个文件,其中文件中的每一行都是图像的路径:

 try(Stream<String> lines = Files.lines(file)){
       List<ImageView> collect = lines
                                  .map(line -> new ImageView( ImageIO.read(new File(line)))
                                  .collect(toList());
  }
Run Code Online (Sandbox Code Playgroud)


Hol*_*ger 8

"可关闭"表示"可以关闭",而不是"必须关闭".

这在过去是正确的,例如见ByteArrayOutputStream:

关闭a ByteArrayOutputStream没有效果.

而且,现在是真正的为StreamS其中的文件明确:

Streams有一个BaseStream.close()方法和实现AutoCloseable,但几乎所有的流实例实际上都不需要在使用后关闭.通常,只有源为IO通道的流(例如返回的流Files.lines(Path, Charset))才需要关闭.

因此,如果审计工具生成错误警告,则是审计工具的问题,而不是API的问题.

请注意,即使您要添加资源管理,也不需要嵌套try语句.虽然以下就足够了:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1)) {
    System.out.println(stream.filter(s->s.contains("Oracle")).count());
}
Run Code Online (Sandbox Code Playgroud)

您也可以将辅助Stream资源添加到资源管理中,而无需额外的try:

final Path p = Paths.get(System.getProperty("java.home"), "COPYRIGHT");
try(Stream<String> stream=Files.lines(p, StandardCharsets.ISO_8859_1);
    Stream<String> filtered=stream.filter(s->s.contains("Oracle"))) {
    System.out.println(filtered.count());
}
Run Code Online (Sandbox Code Playgroud)