2016-12-14 5 views
2

制限のないストリームから開始し、limit()メソッドを使用してバインドを設定すると、制限に達するとStreamで使用されたリソースをどのようにクリーンアップできますか?例えば、私はFiles.linesメソッドが何をやろうとしているが、山車、文字列の代わりに、私は次のようになります機能を記述した場合:Java8ストリームの後にクリーンアップ

public static Stream<Float> floats(File f) throws FileNotFoundException { 
     FileInputStream fis = new FileInputStream(f); 
     return Stream.generate(() -> { 

      byte[] buff = new byte[4]; 
      try { fis.read(buff); } 
      catch (Exception e) { e.printStackTrace(); } 
      return ByteBuffer.wrap(buff).getFloat(0); 

     }).limit(f.length()/4); 
    } 

これは、大規模なバイナリファイルをストリーミングする必要があります私がファイルの終わりに達するまでフロートがいっぱいです。フロートを構成する4バイトでファイルの長さをバイトで割ります。しかし、私はこの限界に達するとfis.close()を実行する何らかの方法を見つけたいと思います。 Streams APIにはこれを行う方法がありますか?

+4

'fis'を閉じて、あなたのメソッドがそれを閉じなければならない' Stream'を返すと言うことを文書化する 'onClose'を提供できます。 –

+0

あなたの例外処理は疑わしいものです。 – Holger

答えて

0

私は別の可能性を思いついた。ハックのようなものだが、うまくいくようだ。私はストリームを常にtrueを返すフィルタで実行しますが、その過程でEOFがチェックされ、入力ストリームに達したら入力ストリームを閉じます。私はこれを行うためのよりクリーンな方法がAPIに組み込まれていることを期待していました。私は全く異なるアプローチで使用したい

Stream<Float> answer; 
    FileInputStream fis = new FileInputStream(f); 

    answer = Stream 
      .generate(() -> { 
       byte[] buff = new byte[4]; 
       try { fis.read(buff); } 
       catch (Exception e) { e.printStackTrace(); } 
       return ByteBuffer.wrap(buff).getFloat(0);}) 
      .filter(x -> { 
       try { if (fis.available() == 0) fis.close(); } 
       catch (Exception e) { e.printStackTrace(); } 
       return true;}) 
      .limit(f.length()/4); 

    return answer; 
+1

Sotiriosが提案しているように、なぜonClose()を使用しないのですか?そして、なぜあなたはラファエルの答えを受け入れましたか?明らかに間違っていますか? –

+0

私はそれを受け入れ、onCloseの方法でうまくいくとは間違っているとは気づきませんでしたが、ストリームが限界に達した後に呼び出される必要がある追加のメソッドを持つ新しいクラスを作成する必要はありません。私は内部的にクリーンアップを世話する単一のStreamオブジェクトが必要です。 –

+2

なぜ別のクラスを書く必要がありますか? '(){} {} {} {} {} {} {} {} {}})' '例外をスローします。ところで、InputStream.available()は、あなたが思うことをしません。あなたはその方法を決して使うべきではありません。 –

0

:これで

public static Stream<Float> floats(File f) throws FileNotFoundException { 
    try(FileChannel fch = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { 
     ByteBuffer bb = fch.map(FileChannel.MapMode.READ_ONLY, 0, fch.size()); 
     FloatBuffer fb = bb.asFloatBuffer(); 
     return IntStream.range(0, fb.remaining()).mapToObj(fb::get); 
    } 
    catch(IOException ex) { // or consider to declare "throws IOException" instead 
     FileNotFoundException fnfe = new FileNotFoundException(ex.getMessage()); 
     fnfe.initCause(ex); 
     throw fnfe; 
    } 
} 

のメソッドが返すとき、FileChannelはすでに閉鎖されています。また、潜在的により効率的です。操作の多くのために、元の値がfloat Sがある場合でも、DoubleStreamようにデータを処理する方が効率的かもしれないことに注意し、必要な場合にのみ、floatに戻し、最終結果の種類を絞り込む:

public static DoubleStream floatsAsDouble(File f) throws FileNotFoundException { 
    try(FileChannel fch = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { 
     ByteBuffer bb = fch.map(FileChannel.MapMode.READ_ONLY, 0, fch.size()); 
     FloatBuffer fb = bb.asFloatBuffer(); 
     return IntStream.range(0, fb.remaining()).mapToDouble(fb::get); 
    } 
    catch(IOException ex) { // or consider to declare throwsIOException instead 
     FileNotFoundException fnfe = new FileNotFoundException(ex.getMessage()); 
     fnfe.initCause(ex); 
     throw fnfe; 
    } 
} 
… 
    float sum=(float)floatsAsDouble(new File("path")).sum(); 

Fileの代わりにPathを直接使用することもできます。

+0

戻り値が呼び出されると同時にこれらの両方がファイルチャンネルを閉じるわけではありませんか?そして 'fb :: get'が閉じたリソースで動作するようにしていないので、うまくいきませんか? –

+0

@M。 Prokhorov:それは私の答えでも述べられています: "*これで、メソッドが*を返すときに' FileChannel'は既に閉じられています。後で何かを閉じなければならないことを避けることは意図的である。重要な点は、[FileChannel.map'のドキュメント(https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html#map-java)にあります。 nio.channels.FileChannel。MapMode-long-long-): "*確立されたマッピングは、作成に使用されたファイルチャネルに依存しません。特にチャネルを閉じることは、マッピングの有効性には影響しません。* " – Holger

+0

私は参照してください。それで、ディスク上の場所のように、チャンネルが指していたところを指し示すByteBufferを作成します。 –

関連する問題