在Stream API中使用AutoClosable接口

Ada*_*ker 14 java java-io java-8 java-stream autocloseable

今天我试着重构这个代码,它从目录中的文件中读取id,

Set<Long> ids = new HashSet<>();
for (String fileName : fileSystem.list("my-directory")) {
    InputStream stream = fileSystem.openInputStream(fileName);
    BufferedReader br = new BufferedReader(new InputStreamReader(stream));
    String line;
    while ((line = br.readLine()) != null) {
        ids.add(Long.valueOf(line.trim()));
    }
    br.close();
}
Run Code Online (Sandbox Code Playgroud)

使用流api

Set<Long> ids = fileSystem.list("my-directory").stream()
    .map(fileName -> fileSystem::openInputStream)
    .map(is -> new BufferedReader(new InputStreamReader(is)))
    .flatMap(BufferedReader::lines)
    .map(String::trim)
    .map(Long::valueOf)
    .collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

然后我发现IO流不会被关闭,我没有看到一种简单的方法来关闭它们,因为它们是在管道内创建的.

有任何想法吗?

upd:示例中的FileSystem是HDFS,Files#lines不能使用类似的方法.

Kis*_*kae 17

一旦消耗了流的所有元素,就可以挂钩到流中以"关闭"资源.因此,可以在读取所有行后通过以下修改关闭阅读器:

.flatMap(reader -> reader.lines().onClose(() -> close(reader)))
Run Code Online (Sandbox Code Playgroud)

在哪里close(AutoClosable)处理IOException.

作为概念证明,以下代码和输出已经过测试:

import java.util.stream.Stream;

class Test {
    public static void main(String[] args) {
        Stream.of(1, 2, 3).flatMap(i ->
                Stream.of(i, i * 2).onClose(() ->
                        System.out.println("Closed!")
                )
        ).forEach(System.out::println);
    }
}

1
2
Closed!
2
4
Closed!
3
6
Closed!
Run Code Online (Sandbox Code Playgroud)

  • 使用[this answer](/sf/answers/2256252141/)的`UncheckedCloseable`,你可以简单地使用`.flatMap(br - > br.lines().onClose(UncheckedCloseable.wrap(br) )))`. (4认同)