本文介绍了Java8 Stream文件,如何控制文件的关闭?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个Java8 Stream< FileReader> 并且我将该流用于 map 等等,如何我可以控制流中使用的 FileReader 的关闭吗?

Suppose I have a Java8 Stream<FileReader> and that I use that stream to map and such, how can I control the closing of the FileReaders used in the stream?

注意,我可能无权访问单个 FileReader s,例如:

Note, that I may not have access to the individual FileReaders, for example:

filenames.map(File::new)
    .filter(File::exists)
    .map(f->{
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(f));
        } catch(Exception e) {}
            return Optional.ofNullable(br);
        })
    .filter(Optional::isPresent)
    .map(Optional::get)
    .flatMap(...something that reads the file contents...) // From here, the Stream doesn't content something that gives access to the FileReaders

在做了其他一些映射之后,我终于失去了 FileReader 续集。

After doing some other mappings, etc, I finally lose the FileReaders in the sequel.

我首先想到垃圾收集器能够在需要时执行它,但是当文件名是一个很长的<$ c $时,我遇到了OS描述符耗尽c> Stream 。

I first thought the garbage collector is able to do it when needed, but I've experienced OS descriptor exhaustion when filenames is a long Stream.

推荐答案

关于使用FileReader的一般说明:FileReader在内部使用FileInputStream它会覆盖 finalize(),因此因为它对garbarge收集的影响,特别是在处理大量文件时。

A general note on the use of FileReader: FileReader uses internally a FileInputStream which overrides finalize() and is therefore discouraged to use beacause of the impact it has on garbarge collection especially when dealing with lots of files.

除非你使用的是Java版本在Java 7之前,您应该使用java.nio.files API,创建一个BufferedReader,其中包含

Unless you're using a Java version prior to Java 7 you should use the java.nio.files API instead, creating a BufferedReader with

 Path path = Paths.get(filename);
 BufferedReader br = Files.newBufferedReader(path);

因此,您的流管道的开头看起来应该更像

So the beginning of your stream pipeline should look more like

 filenames.map(Paths::get)
          .filter(Files::exists)
          .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 

现在解决您的问题:

保留原始 Reader 的一种方法是使用元组。元组(或其任何n元变体)通常是处理函数应用程序的多个结果的好方法,因为它是在流管道中完成的:

One way to preserve the original Reader would be to use a Tuple. A tuple (or any n-ary variation of it) is generally a good way to handle multiple results of a function application, as it's done in a stream pipeline:

class ReaderTuple<T> {
   final Reader first;
   final T second;
   ReaderTuple(Reader r, T s){
     first = r;
     second = s;
   }
}

现在你可以将FileReader映射到一个元组了第二项是您当前的流项:

Now you can map the FileReader to a Tuple with the second item being your current stream item:

 filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 
  .filter(Optional::isPresent)
  .map(Optional::get)
  .flatMap(r -> new ReaderTuple(r, yourOtherItem))
  ....
  .peek(rt -> {
    try { 
      rt.first.close()  //close the reader or use a try-with-resources
    } catch(Exception e){}
   })
  ... 

这种方法的问题是,无论何时在流执行期间发生未经检查的异常发生在flatMap和peek之间,读者可能都不会被关闭。

Problem with that approach is, that whenever an unchecked exception occurrs during stream execution betweem the flatMap and the peek, the readers might not be closed.

使用元组的另一种方法是将需要读取器的代码放入试用 - 资源块。这种方法的优势在于您可以控制关​​闭所有读者。

An alternative to use a tuple is to put the code that requires the reader in a try-with-resources block. This approach has the advantage that you're in control to close all readers.

示例1:

 filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try (Reader r = new BufferedReader(new FileReader(p))){

            Stream.of(r)
            .... //put here your stream code that uses the stream

        } catch (IOException e) {
            return Optional.empty();
        }
    }) //reader is implicitly closed here
 .... //terminal operation here

示例2:

filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 
 .filter(Optional::isPresent)
 .map(Optional::get)
 .flatMap(reader -> {
   try(Reader r = reader) {

      //read from your reader here and return the items to flatten

   } //reader is implicitly closed here
  }) 

示例1具有读者肯定关闭的优势。示例2是安全的,除非您在创建读取器和可能失败的try-with-resources块之间放置更多内容。

Example 1 has the advantage that the reader gets certainly closed. Example 2 is safe unless you put something more between the the creation of the reader and the try-with-resources block that may fail.

我个人会选择示例1,并将访问阅读器的代码放在一个单独的函数中,以便代码更易读。

I personally would go for Example 1, and put the code that is accessing the reader in a separate function so the code is better readable.

这篇关于Java8 Stream文件,如何控制文件的关闭?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-18 06:28