Guava MinMaxPriorityQueue und EvictingQueue

Leitfaden für Guava MinMaxPriorityQueue und EvictingQueue

1. Overview

In diesem Artikel werden die KonstrukteEvictingQueue, undMinMaxPriorityQueue aus der Guava-Bibliothek betrachtet. DasEvictingQueue ist eine Implementierung des Ringpufferkonzepts. MitMinMaxPriorityQueue erhalten wir mithilfe der angegebenenComparator. Zugriff auf das niedrigste und größte Element

2. EvictingQueue

Beginnen wir mit der Erstellung. Wenn Sie eine Instanz der Warteschlange erstellen, müssen Sie die maximale Warteschlangengröße als Argument angeben.

Wenn wir denEvictingQueue undthe queue is full, it automatically evicts an element from its head ein neues Element hinzufügen möchten.

Beim Vergleich mit dem Standardverhalten der Warteschlange blockiert das Hinzufügen eines Elements zur vollständigen Warteschlange nicht, sondern entfernt das head-Element und fügt dem tail ein neues Element hinzu.

Wir können uns dieEvictingQueue als einen Ring vorstellen, in den wir Elemente nur im Anhang einfügen. Wenn sich an der Position, an der wir ein neues Element hinzufügen möchten, ein Element befindet, überschreiben wir einfach das vorhandene Element an der angegebenen Position.

Erstellen wir eine Instanz vonEvictingQueue mit der maximalen Größe von 10. Als nächstes werden wir 10 Elemente hinzufügen:

Queue evictingQueue = EvictingQueue.create(10);

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

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

Wenn wir die Standard-Warteschlangenimplementierung hätten, würde das Hinzufügen eines neuen Elements zur vollständigen Warteschlange den Produzenten blockieren.

Dies ist bei der Implementierung vonEvictingQueuenicht der Fall. Wenn Sie ein neues Element hinzufügen, wird der Kopf von diesem entfernt und das neue Element wird dem Schwanz hinzugefügt:

evictingQueue.add(100);

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

Durch die Verwendung vonEvictingQueue als Umlaufpuffer können wir sehr effiziente gleichzeitige Programme erstellen.

3. MinMaxPriorityQueue

DasMinMaxPriorityQueue bietet zeitkonstanten Zugriff auf seine kleinsten und größten Elemente.

Um das kleinste Element zu erhalten, müssen wir die MethodepeekFirst()aufrufen. Um das größte Element zu erhalten, können wir die MethodepeekLast()aufrufen. Beachten Sie, dass diese Elemente nicht aus einer Warteschlange entfernen, sondern nur abrufen.

Die Reihenfolge der Elemente erfolgt nachComparator, die an den Konstruktor dieser Warteschlange übergeben werden müssen.

Nehmen wir an, wir haben eineCustomClass-Klasse mit einemvalue-Feld vom Integer-Typ:

class CustomClass {
    private Integer value;

    // standard constructor, getters and setters
}

Erstellen wir einMinMaxPriorityQueue, das den Komparator fürint-Typen verwendet. Als Nächstes fügen wir der Warteschlange 10 Objekte vom TypCustomClasshinzu:

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

Aufgrund der Eigenschaften vonMinMaxPriorityQueue und übergebenenComparator, ist das Element am Kopf der Warteschlange gleich 1 und das Element am Ende der Warteschlange gleich 10:

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

Da die Kapazität unserer Warteschlange 10 beträgt und wir 10 Elemente hinzugefügt haben, ist die Warteschlange voll. Wenn Sie ein neues Element hinzufügen, wird das letzte Element in der Warteschlange entfernt. Fügen wir einCustomClass hinzu, wobeivalue gleich -1 ist:

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

Nach dieser Aktion wird das letzte Element in der Warteschlange gelöscht, und das neue Element am Ende der Warteschlange ist gleich 9. Der neue Kopf ist -1, da dies das neue kleinste Element gemäß denComparator ist, die wir beim Erstellen unserer Warteschlange übergeben haben:

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

Gemäß der Spezifikation derMinMaxPriorityQueue,in case the queue is full, adding an element that is greater than the currently greatest element will remove that same element – effectively ignoring it.

Fügen wir eine 100-Zahl hinzu und testen Sie, ob sich dieses Element nach diesem Vorgang in der Warteschlange befindet:

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

Wie wir sehen, ist das erste Element in der Warteschlange immer noch gleich -1 und das letzte ist gleich 9. Daher wurde das Hinzufügen einer Ganzzahl ignoriert, da sie größer ist als das bereits größte Element in der Warteschlange.

4. Fazit

In diesem Artikel haben wir uns das KonstruktEvictingQueue undMinMaxPriorityQueue aus der Guava-Bibliothek angesehen.

Wir haben gesehen, wie manEvictingQueue als Umlaufpuffer verwendet, um sehr effiziente Programme zu implementieren.

Wir haben dieMinMaxPriorityQueue in Kombination mit denComparator verwendet, um zeitlich konstanten Zugriff auf das kleinste und größte Element zu erhalten.

Es ist wichtig, die Merkmale beider präsentierter Warteschlangen zu berücksichtigen, da das Hinzufügen eines neuen Elements zu ihnen ein Element überschreibt, das sich bereits in der Warteschlange befindet. Dies steht im Gegensatz zu den Standardimplementierungen für Warteschlangen, bei denen das Hinzufügen eines neuen Elements zur vollständigen Warteschlange den Producer-Thread blockiert oder eine Ausnahme auslöst.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inGitHub project - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.