在 Stream API 中使用 AutoClosable 接口
Using AutoClosable interfaces inside Stream API
今天我尝试重构这段代码,它从目录中的文件中读取 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();
}
使用流 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());
然后我发现 IO 流不会被关闭并且我没有看到关闭它们的简单方法,因为它们是在管道内部创建的。
有什么想法吗?
upd:例子中的文件系统是HDFS,Files#lines
等类似的方法不能使用
我还没有测试过实际的代码,但也许是这样的?
Set<Long> ids = fileSystem.list("my-directory").stream()
.map(fileName -> fileSystem::openInputStream)
.flatMap(is -> {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
return is.lines().map(String::trim).map(Long::valueOf);
}
})
.collect(Collectors.toSet());
当然没有你的漂亮,但我相信它是最接近你的了。
为什么不简单一点,通过 Files.lines
:
try (Stream<String> s = Files.lines(Paths.get("yourpath" + fileName))) {
s.map(String::trim)
.map(Long::valueOf)
.collect(Collectors.toSet());
}
一旦流的所有元素都被消耗,就可以将流连接到 'close' 资源。因此,可以通过以下修改在读取所有行后关闭 reader:
.flatMap(reader -> reader.lines().onClose(() -> close(reader)))
其中 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!
今天我尝试重构这段代码,它从目录中的文件中读取 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();
}
使用流 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());
然后我发现 IO 流不会被关闭并且我没有看到关闭它们的简单方法,因为它们是在管道内部创建的。
有什么想法吗?
upd:例子中的文件系统是HDFS,Files#lines
等类似的方法不能使用
我还没有测试过实际的代码,但也许是这样的?
Set<Long> ids = fileSystem.list("my-directory").stream()
.map(fileName -> fileSystem::openInputStream)
.flatMap(is -> {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
return is.lines().map(String::trim).map(Long::valueOf);
}
})
.collect(Collectors.toSet());
当然没有你的漂亮,但我相信它是最接近你的了。
为什么不简单一点,通过 Files.lines
:
try (Stream<String> s = Files.lines(Paths.get("yourpath" + fileName))) {
s.map(String::trim)
.map(Long::valueOf)
.collect(Collectors.toSet());
}
一旦流的所有元素都被消耗,就可以将流连接到 'close' 资源。因此,可以通过以下修改在读取所有行后关闭 reader:
.flatMap(reader -> reader.lines().onClose(() -> close(reader)))
其中 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!