2016-02-05 9 views
6

ストリームはどのように停止しますか?私は標準ライブラリ停止している<code>Stream.generate</code>どのようにストリームと自分の無限のストリームを作成したときに、レコードのリストを持っているとき、私はたとえば...</p> <p>を思っていた

List<Record> records = getListWithRecords(); 
records.stream().forEach(/* do something */); 

ストリーム永遠に無限に実行されることはありませんが、リスト内のすべての項目が横断されると停止します。しかし、それはどのように機能しますか?同じ機能がFiles.lines(path)(ソース:http://www.mkyong.com/java8/java-8-stream-read-a-file-line-by-line/)によって作成されたストリームに適用されます。

また、Stream.generateで作成されたストリームは、同じ方法でどのように停止することができますか?

答えて

9

有限ストリームは、Stream.generateで作成されません。

ストリームを実装する標準的な方法は、Spliteratorを実装することです。the Iterator detourを使用することもあります。どちらの場合でも、実装には終了を報告する方法があります。 Spliterator.tryAdvancefalse、またはそのforEachRemainingメソッドが返されたとき、またはIteratorのソースの場合、hasNext()falseを返したとき。

Spliteratorは、処理が開始される前に予想される要素の数を報告する場合もあります。 Stream.generateSpliterator同様またはストリーム実装の内部機能を使用して、どちらか実装されてもよいが、関係なく、それらが実装されているかの、あなたは「ドンのようなStreamインターフェース内の工場のいずれかの方法で作成された

ストリーム、そのようなストリームを有限にする唯一の方法は、limit操作をストリームに連結することです。

配列またはコレクションの背後にない空でない空のストリームを作成し、既存のストリームソースのいずれも適合しない場合は、独自のSpliteratorおよびcreate a stream out of itを実装する必要があります。上記のように、既存の方法を使用してIteratorからSpliteratorを作成することはできますが、慣れているという理由でIteratorを使用する誘惑に抵抗する必要があります。あなたは、例えばdefaultSpliteratorインタフェース、重み付け開発費の方法と潜在的なパフォーマンスの向上、の上書きを追加することができ、この出発点から

/** like {@code Stream.generate}, but with an intrinsic limit */ 
static <T> Stream<T> generate(Supplier<T> s, long count) { 
    return StreamSupport.stream(
       new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) { 
     long remaining=count; 

     public boolean tryAdvance(Consumer<? super T> action) { 
      if(remaining<=0) return false; 
      remaining--; 
      action.accept(s.get()); 
      return true; 
     } 
    }, false); 
} 

Spliteratorは、実装することは難しいことではありません

static <T> Stream<T> generate(Supplier<T> s, long count) { 
    return StreamSupport.stream(
       new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) { 
     long remaining=count; 

     public boolean tryAdvance(Consumer<? super T> action) { 
      if(remaining<=0) return false; 
      remaining--; 
      action.accept(s.get()); 
      return true; 
     } 

     /** May improve the performance of most non-short-circuiting operations */ 
     @Override 
     public void forEachRemaining(Consumer<? super T> action) { 
      long toGo=remaining; 
      remaining=0; 
      for(; toGo>0; toGo--) action.accept(s.get()); 
     } 
    }, false); 
} 
+1

なぜspliteratorを定義するためにイテレータを使用しないように?私はBufferedReader.lines()がこのアプローチを使用して有限ストリームを作成することを見てきました。 – Juru

+5

'BufferedReader.lines()'は良い例です。 'next()'と '' hasNext() 'の実装を見てください(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/io /BufferedReader.java?av = f#566)と、呼び出し間の状態をどのように保つ必要があるのか​​を示します。対照的に、スプライテータは単純なものですが、単一のメソッドが必要です: 'tryAdvance(Consumer <?super String> c){String line = readLine(); if(line == null)falseを返します。 c.accept(line);真を返します。 } 'それはそれです。実装が簡単で(例外処理を追加し、コードサイズの半分を追加する)、ラッパーは必要ありません。 – Holger

+0

この実装はスレッドセーフですか?それは必要ですか? – WillD

0

私はこの

​​

のための一般的な回避策を作成している使い方は非常に簡単です:

GuardedSpliterator<Integer> source = new GuardedSpliterator<>(
    () -> rnd.nextInt(), 
    (i) -> i > 10, 
    true 
); 

Stream<Integer> ints = StreamSupport.stream(source, false); 

ints.forEach(i -> System.out.println(i));  
関連する問題