web-dev-qa-db-ja.com

ストリームAPIとキュー:BlockingQueueストリームスタイルをサブスクライブする

キューがあるとしましょう

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));
24

私はあなたが何を期待しているのか少し推測していますが、私は良い予感があると思います。

キューの反復と同様に、キューのストリームは、キューの現在の内容を表します。イテレータまたはストリームがキューの最後に到達しても、追加される要素を待ってブロックすることはありません。その時点でイテレータまたはストリームが使い果たされ、計算が終了します。

キューの現在および将来のすべての要素で構成されるストリームが必要な場合は、次のようなことができます。

Stream.generate(() -> {
        try {
            return queue.take();
        } catch (InterruptedException ie) {
            return "Interrupted!";
        }
    })
    .filter(s -> s.endsWith("x"))
    .forEach(System.out::println);   

(残念ながらInterruptedExceptionを処理する必要があるため、これはかなり厄介です。)

キューを閉じる方法はなく、Stream.generateが要素の生成を停止する方法がないため、これは事実上無限ストリームです。これを終了する唯一の方法は、findFirstなどのショートストリームストリーム操作を使用することです。

32
Stuart Marks

非同期キューの実装を見ることができます。 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();
14
John McClean

別のアプローチは、カスタム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(...))を介してストリームを構築し、通常のストリーム操作を追加できます。

11
user3787629