1 。 概要

この記事では、Guavaライブラリからの EvictingQueue、、およびMinMaxPriorityQueueコンストラクトについて説明します。 EvictingQueue は、循環バッファーの概念の実装です。 MinMaxPriorityQueue は、提供されたコンパレータを使用して最小および最大の要素へのアクセスを提供します。

2. EvictingQueue

構築から始めましょう–キューのインスタンスを構築するとき、引数として最大キューサイズを指定する必要があります。

EvictingQueue に新しいアイテムを追加したいときに、キューがいっぱいになると、そのヘッドから要素が自動的に削除されます。

標準のキューの動作と比較すると、キュー全体に要素を追加してもブロックされませんが、head要素が削除され、新しいアイテムがtailに追加されます。

EvictingQueue は、追加のみの方法で要素を挿入するリングとして想像できます。 新しい要素を追加する位置に要素がある場合は、指定された位置にある既存の要素をオーバーライドするだけです。

最大サイズが10のEvictingQueueのインスタンスを作成してみましょう。 次に、それに10個の要素を追加します。

Queue<Integer> evictingQueue = EvictingQueue.create(10);

IntStream.range(0, 10)
  .forEach(evictingQueue::add);

assertThat(evictingQueue)
  .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

標準のキュー実装がある場合、完全なキューに新しいアイテムを追加すると、プロデューサーがブロックされます。

これは、EvictingQueueの実装には当てはまりません。 新しい要素を追加すると、ヘッドが削除され、新しい要素がテールに追加されます。

evictingQueue.add(100);

assertThat(evictingQueue)
  .containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9, 100);

EvictingQueue を循環バッファーとして使用することにより、非常に効率的な並行プログラムを作成できます。

3. MinMaxPriorityQueue

MinMaxPriorityQueue は、最小要素と最大要素への常時アクセスを提供します。

最小の要素を取得するには、 peekFirst()メソッドを呼び出す必要があります。 最大の要素を取得するには、 peekLast()メソッドを呼び出すことができます。 これらはキューから要素を削除するのではなく、それを取得するだけであることに注意してください。

要素の順序付けは、このキューのコンストラクターに渡す必要があるコンパレーターによって行われます。

整数型のフィールドを持つCustomClassクラスがあるとしましょう。

class CustomClass {
    private Integer value;

    // standard constructor, getters and setters
}

intタイプのコンパレータを使用するMinMaxPriorityQueueを作成しましょう。 次に、CustomClassタイプの10個のオブジェクトをキューに追加します。

MinMaxPriorityQueue<CustomClass> queue = MinMaxPriorityQueue
  .orderedBy(Comparator.comparing(CustomClass::getValue))
  .maximumSize(10)
  .create();

IntStream
  .iterate(10, i -> i - 1)
  .limit(10)
  .forEach(i -> queue.add(new CustomClass(i)));

MinMaxPriorityQueue と渡されたコンパレータの特性により、キューの先頭の要素は1に等しく、キューの末尾の要素は10に等しくなります。 :

assertThat(
  queue.peekFirst().getValue()).isEqualTo(1);
assertThat(
  queue.peekLast().getValue()).isEqualTo(10);

キューの容量が10で、10個の要素を追加したため、キューがいっぱいになりました。 新しい要素を追加すると、キューの最後の要素が削除されます。 valueが-1に等しいCustomClassを追加しましょう。

queue.add(new CustomClass(-1));

そのアクションの後、キューの最後の要素が削除され、その末尾の新しいアイテムは9に等しくなります。 これは、キューを作成したときに渡したコンパレータによる新しい最小要素であるため、新しいヘッドは-1になります。

assertThat(
  queue.peekFirst().getValue()).isEqualTo(-1);
assertThat(
  queue.peekLast().getValue()).isEqualTo(9);

MinMaxPriorityQueueの仕様によると、 キューがいっぱいの場合、現在最大の要素よりも大きい要素を追加すると、同じ要素が削除され、事実上無視されます。

100の数字を追加して、その操作の後にそのアイテムがキューにあるかどうかをテストしてみましょう。

queue.add(new CustomClass(100));
assertThat(queue.peekFirst().getValue())
  .isEqualTo(-1);
assertThat(queue.peekLast().getValue())
  .isEqualTo(9);

ご覧のとおり、キューの最初の要素はまだ-1に等しく、最後の要素は9に等しくなります。 したがって、整数の追加は、キュー内のすでに最大の要素よりも大きいため、無視されました。

4. 結論

この記事では、GuavaライブラリのEvictingQueueおよびMinMaxPriorityQueueコンストラクトについて説明しました。

EvictingQueue を循環バッファーとして使用して、非常に効率的なプログラムを実装する方法を見てきました。

MinMaxPriorityQueueComparatorと組み合わせて使用し、最小要素と最大要素に常時アクセスできるようにしました。

新しい要素を追加すると、すでにキューにある要素が上書きされるため、提示された両方のキューの特性を覚えておくことが重要です。 これは、キュー全体に新しい要素を追加するとプロデューサースレッドがブロックされるか、例外がスローされる標準のキュー実装とは異なります。

これらすべての例とコードスニペットの実装は、 GitHubプロジェクトにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。