キューがあるとしましょう
BlockingQueue<String> queue= new LinkedBlockingQueue<>();
そして、他のいくつかのスレッドがそれに値を入れて、それを次のように読みます
while (true) {
String next = queue.take();
System.out.println("next message:" + next);
}
上記のコードと同様のセマンティクスを維持しながら、ストリームスタイルでこのキューを反復処理する方法を教えてください。
このコードは、現在のキューの状態のみをトラバースします。
queue.stream().forEach(e -> System.out.println(e));
私はあなたが何を期待しているのか少し推測していますが、私は良い予感があると思います。
キューの反復と同様に、キューのストリームは、キューの現在の内容を表します。イテレータまたはストリームがキューの最後に到達しても、追加される要素を待ってブロックすることはありません。その時点でイテレータまたはストリームが使い果たされ、計算が終了します。
キューの現在および将来のすべての要素で構成されるストリームが必要な場合は、次のようなことができます。
Stream.generate(() -> {
try {
return queue.take();
} catch (InterruptedException ie) {
return "Interrupted!";
}
})
.filter(s -> s.endsWith("x"))
.forEach(System.out::println);
(残念ながらInterruptedException
を処理する必要があるため、これはかなり厄介です。)
キューを閉じる方法はなく、Stream.generate
が要素の生成を停止する方法がないため、これは事実上無限ストリームです。これを終了する唯一の方法は、findFirst
などのショートストリームストリーム操作を使用することです。
非同期キューの実装を見ることができます。 Java 8、次に cyclops-react がある場合、私はこのプロジェクトの開発者です。async.Queueを提供します。これにより、非同期に(そしてきれいに)キューに入れます。
例えば.
Queue<String> queue = QueueFactories.<String>unboundedQueue().build();
または単に(これがcom.aol.simple.react.async.Queueである限り)
Queue<String> queue = new Queue<>();
次に、別のスレッドで:
new Thread(() -> {
while (true) {
queue.add("New message " + System.currentTimeMillis());
}
}).start();
メインスレッドに戻ると、元のコードは期待どおりに機能するはずです(キューに追加されているメッセージを細かく繰り返し処理して出力します)。
queue.stream().forEach(e -> System.out.println(e));
キュー、したがってストリームは-を介して任意の段階で閉じることができます
queue.close();
別のアプローチは、カスタムSpliteratorを構築することです。私の場合、ブロッキングキューがあり、ブロックがタイムアウトするまで、要素を抽出し続けるストリームを構築したいと考えています。スプリッターは次のようなものです:
public class QueueSpliterator<T> implements Spliterator<T> {
private final BlockingQueue<T> queue;
private final long timeoutMs;
public QueueSpliterator(final BlockingQueue<T> queue, final long timeoutMs) {
this.queue = queue;
this.timeoutMs = timeoutMs;
}
@Override
public int characteristics() {
return Spliterator.CONCURRENT | Spliterator.NONNULL | Spliterator.ORDERED;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public boolean tryAdvance(final Consumer<? super T> action) {
try {
final T next = this.queue.poll(this.timeoutMs, TimeUnit.MILLISECONDS);
if (next == null) {
return false;
}
action.accept(next);
return true;
} catch (final InterruptedException e) {
throw new SupplierErrorException("interrupted", e);
}
}
@Override
public Spliterator<T> trySplit() {
return null;
}
}
InterruptedExceptionを処理するためにスローされる例外は、RuntimeExceptionの拡張です。このクラスを使用すると、StreamSupport.stream(new QueueSpliterator(...))を介してストリームを構築し、通常のストリーム操作を追加できます。