2016-12-02 6 views
2

Java 8 Stream.iterator()は終了時にストリームを自動クローズしますか? 私はこのようなものはありませ...リソースを自動クローズするJava 8ストリーム.iterator()?

を考える:

class Provider implements Serializable { 

    Iterator<String> iterator() { 
    Stream<String> stream = new BufferedReader(...).lines(); 
    return stream.iterator(); 
    } 
} 

このイテレータは、イテレータがファイル読み取りリソースに基づいて把握していない他のいくつかのクラスによって使用されています。つまり、ファイルが大きすぎてメモリに格納できないため、ファイルをストリームする必要があります。しかし、イテレータがそれ以上の要素を持たないときにリソースを自動クローズして、リソースが漏れていないようにしたいと考えています。 ProviderクラスはSerializableであり、StreamまたはBufferedReaderをメンバーとして持つことはできません。

これを行うには良い方法がありますか?

+4

「AutoClosingIterator」ラッパーを作成することができます。しかし、私はそれが壊れやすいのでそれをお勧めしません:iteratorの消費者がそれを使い果たす前に失敗した場合、あなたはリソースリークを取得します。 –

+0

提案してくれてありがとう - 私にもう少しコントロールを与える何か他に? – webuster

+2

イテレータを放棄する消費者からあなたを救うことはできません。消費者の契約を変更するだけで助けになります。 –

答えて

2

まず、BufferedReader.linesから作成したストリームは、このようにストリームは効果がありません閉じて、すべてのリソースを保持していないことに注意してください:ストリームがリソースを保持している場合

BufferedReader br = new BufferedReader(...); 
try(Stream<String> stream = br.lines()) { 
    ... use stream 
} 
// br is still open here! 

は、通常は明示的に文書化されています。例えば、 Files.linesドキュメント本:

返されるストリームはReaderをカプセル化します。ファイルシステムリソースのタイムリーな処分が必要な場合は、ストリーム操作の完了後にストリームのcloseメソッドが確実に呼び出されるように、try-with-resources構造を使用する必要があります。

BufferedReader.linesのドキュメントにそのような発言はありません。

したがって、実際には閉鎖する必要のあるリソースがある場合は、BufferedReaderを閉じることはあなたの責任です。それは常にそうではありません。たとえば、new BufferedReader(new StringReader(string))を作成する場合は、閉じるリソースがないため、close()メソッドを呼び出さないようにしてください。

とにかく、質問に戻る。ストリームに実際にリソース(たとえばFiles.lines()から作成されたもの)が実際に保持されていると仮定すると、イテレータが最後まで移動されたかどうかにかかわらず、イテレータを返すだけではストリームは自動的に閉じられません。特定の瞬間にストリームを閉じるには、ストリーム上でclose()メソッドを明示的に呼び出す必要があります。さもなければ、ガベージコレクタに頼らざるを得なくなり、最終的に基本リソースオブジェクト(例えば、FileInputStream)をファイナライズキューに入れ、最終的にファイルを閉じるオブジェクトのfinalizeメソッドを呼び出します。これがいつ発生するか保証することはできません。

代わりに、入力ファイル全体をメモリにバッファリングし(非常に長くないと仮定して)、イテレータを返す前にファイルを閉じることができます。ストリームAPIなしでファイルを読み込むためにこれを行うことができます:

return Files.readAllLines(pathToTheFile).iterator();