Javaストリームを閉じる必要がありますか?
1. 概要
Java 8にラムダ式が導入されたことで、より簡潔で機能的な方法でコードを記述できるようになりました。 StreamsおよびFunctionalInterfaces は、Javaプラットフォームにおけるこの革新的な変更の中心です。
このクイックチュートリアルでは、リソースの観点からJava8ストリームを明示的に閉じる必要があるかどうかを学習します。
2. ストリームを閉じる
Java 8 ストリームは、AutoCloseableインターフェースを実装します。
public interface Stream<T> extends BaseStream<...> {
// omitted
}
public interface BaseStream<...> extends AutoCloseable {
// omitted
}
簡単に言えば、
これは最初は直感に反するように聞こえるかもしれません。そこで、Java8ストリームを閉じる必要がある場合と閉じない場合を見てみましょう。
2.1. コレクション、配列、およびジェネレーター
ほとんどの場合、Javaコレクション、配列、またはジェネレーター関数からStreamインスタンスを作成します。 たとえば、ここでは、StreamAPIを介してStringのコレクションを操作しています。
List<String> colors = List.of("Red", "Blue", "Green")
.stream()
.filter(c -> c.length() > 4)
.map(String::toUpperCase)
.collect(Collectors.toList());
場合によっては、有限または無限のシーケンシャルストリームを生成しています。
Random random = new Random();
random.ints().takeWhile(i -> i < 1000).forEach(System.out::println);
さらに、配列ベースのストリームを使用することもできます。
String[] colors = {"Red", "Blue", "Green"};
Arrays.stream(colors).map(String::toUpperCase).toArray()
2.2. IOリソース
ただし、一部のストリームは、ファイルやソケットなどのIOリソースによってサポートされています。 たとえば、 Files.lines()メソッドは、指定されたファイルのすべての行をストリーミングします。
Files.lines(Paths.get("/path/to/file"))
.flatMap(line -> Arrays.stream(line.split(",")))
// omitted
内部的には、このメソッドは FileChannel インスタンスを開き、ストリームが閉じられると閉じます。 したがって、ストリームを閉じるのを忘れると、基になるチャネルは開いたままになり、リソースリークが発生します。
このようなリソースリークを防ぐために、try-with-resourcesイディオムを使用してIOベースのストリームを閉じることを強くお勧めします。
try (Stream<String> lines = Files.lines(Paths.get("/path/to/file"))) {
lines.flatMap(line -> Arrays.stream(line.split(","))) // omitted
}
このようにして、コンパイラはチャネルを自動的に閉じます。 ここで重要なポイントは、すべてのIOベースのストリームを閉じることです。
すでに閉じられているストリームを閉じると、IllegalStateExceptionがスローされることに注意してください。
3. 結論
この短いチュートリアルでは、単純なストリームとIOが多いストリームの違いを確認しました。 また、これらの違いが、Java8ストリームを閉じるかどうかの決定にどのように影響するかを学びました。
いつものように、サンプルコードはGitHubでから入手できます。