Streamのjavadocの状態:
ストリームにはBaseStream.close()メソッドがあり、AutoCloseableを実装していますが、ほとんどすべてのストリームインスタンスを実際に使用後に閉じる必要はありません。一般に、ソースがIOチャネル(Files.lines(Path、Charset)によって返されるものなど))であるストリームのみを閉じる必要があります。ほとんどのストリームは、コレクション、配列、または生成関数によってサポートされます。 、特別なリソース管理を必要としません(ストリームを閉じる必要がある場合、try-with-resourcesステートメントでリソースとして宣言できます)。
したがって、ほとんどの場合、collection.stream().forEach(System.out::println);
のようなワンライナーでStreamsを使用できますが、Files.lines
およびその他のリソースに裏打ちされたストリームでは、try-with-resourcesステートメントを使用する必要がありますまたは、リソースをリークします。
これはエラーが発生しやすく、不必要だと思います。 Streamsは1回しか反復できないため、Files.lines
の出力を反復したらすぐに閉じてはならない状況はないようです。したがって、実装は暗黙的にcloseを暗黙的に呼び出す必要があります。端末操作の終了。私は間違っていますか?
はい、これは意図的な決定でした。両方の選択肢を検討しました。
ここでの運用設計の原則は、「リソースを取得した人は誰でもリソースを解放する」ことです。 EOFを読み取ってもファイルは自動的に閉じません。ファイルを開いた人がファイルを明示的に閉じることを期待しています。 IOリソースによって支援されるストリームは同じです。
幸いなことに、言語はこれを自動化するためのメカニズムtry-with-resourcesを提供します。 StreamはAutoCloseableを実装しているため、次のことができます。
try (Stream<String> s = Files.lines(...)) {
s.forEach(...);
}
「オートクローズすることは本当に便利なので、ワンライナーとして書くことができる」という議論はニースですが、ほとんどは犬を振る尾です。ファイルまたは他のリソースを開いた場合は、それを閉じる準備もする必要があります。効果的で一貫性のあるリソース管理は「これを1行で記述したい」よりも優先されます。
@BrianGoetzの回答に加えて、より具体的な例があります。 Stream
にはiterator()
のようなエスケープハッチメソッドがあることを忘れないでください。あなたがこれをしているとします:
_Iterator<String> iterator = Files.lines(path).iterator();
_
その後、hasNext()
とnext()
を数回呼び出してから、このイテレータを放棄するだけです:Iterator
インターフェイスはそのような使用を完全にサポートします。 Iterator
を明示的に閉じる方法はありません。ここで閉じることができるオブジェクトはStream
だけです。このように、それは完全にうまく機能します:
_try(Stream<String> stream = Files.lines(path)) {
Iterator<String> iterator = stream.iterator();
// use iterator in any way you want and abandon it at any moment
} // file is correctly closed here.
_
さらに、「1行の書き込み」が必要な場合。これを行うことができます:
Files.readAllLines(source).stream().forEach(...);
ファイル全体が必要で、ファイルが小さいことが確実な場合に使用できます。遅延読み取りではないからです。
私のように怠け者で、「例外が発生した場合、ファイルハンドルが開いたままになる」ことを気にしない場合は、次のようなオートクロージングストリームでストリームをラップできます(他の方法もあります)。
static Stream<String> allLinesCloseAtEnd(String filename) throws IOException {
Stream<String> lines = Files.lines(Paths.get(filename));
Iterator<String> linesIter = lines.iterator();
Iterator it = new Iterator() {
@Override
public boolean hasNext() {
if (!linesIter.hasNext()) {
lines.close(); // auto-close when reach end
return false;
}
return true;
}
@Override
public Object next() {
return linesIter.next();
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
}