Java 关闭管道中间的小溪
当我执行这段代码时,它会在流管道中打开许多文件:Java 关闭管道中间的小溪,java,java-8,java-stream,Java,Java 8,Java Stream,当我执行这段代码时,它会在流管道中打开许多文件: publicstaticvoidmain(字符串[]args)引发IOException{ Files.find(path.get(“JAVA\u DOCS\u DIR/DOCS/api/”), 100,(path,attr)->path.toString().endsWith(“.html”)) .map(文件->runtimizeException(()->Files.line(文件,StandardCharsets.ISO_8859_1))
publicstaticvoidmain(字符串[]args)引发IOException{
Files.find(path.get(“JAVA\u DOCS\u DIR/DOCS/api/”),
100,(path,attr)->path.toString().endsWith(“.html”))
.map(文件->runtimizeException(()->Files.line(文件,StandardCharsets.ISO_8859_1)))
.map(流::计数)
.forEachOrdered(System.out::println);
}
我得到一个例外:
java.nio.file.FileSystemException:/long/file/name:打开的文件太多
问题在于,Stream.count
在遍历流时不会关闭流。但我不明白为什么不应该,因为这是一个终端操作。其他终端操作(如reduce
和forEach
)也是如此<另一方面,code>flatMap关闭它所包含的流
文档告诉我在必要时使用try with resources语句来关闭流。在我的例子中,我可以用如下内容替换count
行:
Files.find(Paths.get("Java_8_API_docs/docs/api"), 100,
(path, attr) -> path.toString().endsWith(".html"))
.map(file -> applyAndClose(
() -> Files.lines(file, StandardCharsets.ISO_8859_1),
Stream::count))
.forEachOrdered(System.out::println);
/**
* Applies a function to a resource and closes it afterwards.
* @param sup Supplier of the resource that should be closed
* @param op operation that should be performed on the resource before it is closed
* @return The result of calling op.apply on the resource
*/
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
try (A res = sup.call()) {
return op.apply(res);
} catch (RuntimeException exc) {
throw exc;
} catch (Exception exc) {
throw new RuntimeException("Wrapped in applyAndClose", exc);
}
}
Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
100, (path, attr) -> path.toString().endsWith(".html"))
.mapToLong(this::lineCount)
.forEachOrdered(System.out::println);
.map(s->{long c=s.count();s.close();返回c;})
但这既嘈杂又丑陋,在某些情况下,对于大型复杂的管道来说,这可能是一个真正的不便
因此,我的问题如下:
RuntimeException
是一种将选中的异常包装在RuntimeException
s中的方法。您需要在此流操作中调用close()
,这将导致调用所有底层关闭处理程序
更好的方法是将整个语句包装在try with resources块中,这样它将自动调用close处理程序
这在您的情况下可能不太可能,这意味着您需要在某些操作中自行处理。您当前的方法可能根本不适合流
似乎您确实需要在第二次map()
操作中执行此操作。仅应调用一次关闭接口AutoCloseable。有关详细信息,请参阅的文档
如果最终操作将自动关闭流,则可能会调用close两次。请看以下示例:
try (Stream<String> lines = Files.lines(path)) {
lines.count();
}
try(流行=文件。行(路径)){
line.count();
}
正如现在定义的那样,第行上的close方法将只调用一次。无论最终操作是否正常完成,或操作是否因IOException异常而中止。如果流在最终操作中被隐式关闭,则如果发生IOException,将调用close方法一次,如果操作成功完成,将调用close方法两次。这里有一个替代方法,它使用
文件中的另一个方法,并将避免泄漏文件描述符:
Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
100, (path, attr) -> path.toString().endsWith(".html"))
.map(file -> runtimizeException(() -> Files.readAllLines(file, StandardCharsets.ISO_8859_1).size())
.forEachOrdered(System.out::println);
与您的版本不同,它将返回int
而不是long
作为行计数;但是,你没有那么多行的文件,是吗? 可以创建一个实用的方法,它可以可靠地关闭管道中间的流。
这样可以确保每个资源都使用try with resource语句关闭,但不需要自定义实用程序方法,并且比直接在lambda中编写try语句要简单得多
使用此方法,问题的管道如下所示:
Files.find(Paths.get("Java_8_API_docs/docs/api"), 100,
(path, attr) -> path.toString().endsWith(".html"))
.map(file -> applyAndClose(
() -> Files.lines(file, StandardCharsets.ISO_8859_1),
Stream::count))
.forEachOrdered(System.out::println);
/**
* Applies a function to a resource and closes it afterwards.
* @param sup Supplier of the resource that should be closed
* @param op operation that should be performed on the resource before it is closed
* @return The result of calling op.apply on the resource
*/
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
try (A res = sup.call()) {
return op.apply(res);
} catch (RuntimeException exc) {
throw exc;
} catch (Exception exc) {
throw new RuntimeException("Wrapped in applyAndClose", exc);
}
}
Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
100, (path, attr) -> path.toString().endsWith(".html"))
.mapToLong(this::lineCount)
.forEachOrdered(System.out::println);
实现如下所示:
Files.find(Paths.get("Java_8_API_docs/docs/api"), 100,
(path, attr) -> path.toString().endsWith(".html"))
.map(file -> applyAndClose(
() -> Files.lines(file, StandardCharsets.ISO_8859_1),
Stream::count))
.forEachOrdered(System.out::println);
/**
* Applies a function to a resource and closes it afterwards.
* @param sup Supplier of the resource that should be closed
* @param op operation that should be performed on the resource before it is closed
* @return The result of calling op.apply on the resource
*/
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
try (A res = sup.call()) {
return op.apply(res);
} catch (RuntimeException exc) {
throw exc;
} catch (Exception exc) {
throw new RuntimeException("Wrapped in applyAndClose", exc);
}
}
Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
100, (path, attr) -> path.toString().endsWith(".html"))
.mapToLong(this::lineCount)
.forEachOrdered(System.out::println);
/**
*将函数应用于资源,然后将其关闭。
*@param sup应关闭的资源的供应商
*@param op应在资源关闭前对其执行的操作
*@返回对资源调用op.apply的结果
*/
专用静态B applyAndClose(可调用支持,函数op){
try(res=sup.call()){
返回op.apply(res);
}捕获(RuntimeException exc){
抛出exc;
}捕获(异常exc){
抛出新的RuntimeException(“包装在applyAndClose中”,exc);
}
}
(由于需要关闭的资源在分配时通常也会抛出异常,因此非运行时异常被包装在运行时异常中,从而避免了使用单独的方法来完成此操作。)这里有两个问题:处理检查过的异常,如IOException
,以及及时关闭资源
所有预定义的功能接口都没有声明任何已检查的异常,这意味着它们必须在lambda中处理,或者包装在未检查的异常中并重新启动。看起来您的runtimizeException
函数就是这样做的。您可能还必须为它声明自己的函数接口。正如你可能已经发现的,这是一种痛苦
关于关闭文件之类的资源,有一些调查表明,当到达流的末尾时,流将自动关闭。这会很方便,但不会处理抛出异常时的关闭。在streams中没有神奇的“做正确的事情”机制
我们只剩下处理资源闭包的标准Java技术,即Java 7中引入的try with resources构造。TWR确实希望在调用堆栈中关闭与打开相同级别的资源。适用“谁打开它,谁就必须关闭它”的原则。TWR还处理异常处理,这通常使在同一位置处理异常处理和资源关闭变得很方便
在本例中,流有点不寻常,因为它将流
映射到流
。这些嵌套流是未关闭的流,导致ev