Guide de la file d’attente MinMaxPriorityQueue et EvictingQueue de Guava

Guide de la file d'attente MinMaxPriorityQueue et EvictingQueue de Guava

1. Overview

Dans cet article, nous allons examiner les constructionsEvictingQueue, etMinMaxPriorityQueue de la bibliothèque Guava. LeEvictingQueue est une implémentation du concept de tampon circulaire. LeMinMaxPriorityQueue nous donne un accès à son élément le plus bas et le plus grand en utilisant lesComparator. fournis

2. EvictingQueue

Commençons par la construction - lors de la construction d'une instance de la file d'attente, nous devons fournir la taille maximale de la file d'attente comme argument.

Lorsque nous voulons ajouter un nouvel élément auxEvictingQueue etthe queue is full, it automatically evicts an element from its head.

Lors de la comparaison avec le comportement de la file d'attente standard, l'ajout d'un élément à la file d'attente complète ne bloque pas, mais supprime l'élément de tête et ajoute un nouvel élément à la fin.

Nous pouvons imaginer lesEvictingQueue comme un anneau dans lequel nous insérons des éléments de manière append-only. S'il y a un élément sur la position sur lequel nous voulons ajouter un nouvel élément, nous substituons simplement l'élément existant à la position donnée.

Construisons une instance desEvictingQueue avec une taille maximale de 10. Ensuite, nous ajouterons 10 éléments:

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

Si nous avions l'implémentation de la file d'attente standard, l'ajout d'un nouvel élément à la file d'attente entière bloquerait le producteur.

Ce n'est pas un cas avec l'implémentation deEvictingQueue. L'ajout d'un nouvel élément à celui-ci entraînera la suppression de la tête, et le nouvel élément sera ajouté à la queue:

evictingQueue.add(100);

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

En utilisant lesEvictingQueue comme tampon circulaire, nous pouvons créer des programmes concurrents très efficaces.

3. MinMaxPriorityQueue

LeMinMaxPriorityQueue fournit un accès en temps constant à ses plus petits et plus grands éléments.

Pour obtenir le moindre élément, nous devons appeler la méthodepeekFirst(). Pour obtenir le plus grand élément, nous pouvons appeler la méthodepeekLast(). Notez que ceux-ci ne suppriment pas les éléments d'une file d'attente, ils ne la récupèrent que.

L'ordre des éléments est effectué par lesComparator qui doivent être passés au constructeur de cette file d'attente.

Disons que nous avons une classeCustomClass qui a un champvalue de type entier:

class CustomClass {
    private Integer value;

    // standard constructor, getters and setters
}

Créons unMinMaxPriorityQueue qui utilisera le comparateur sur les typesint. Ensuite, nous ajouterons 10 objets de typeCustomClass à la file d'attente:

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

En raison des caractéristiques desMinMaxPriorityQueue et desComparator, passés, l'élément en tête de file d'attente sera égal à 1 et l'élément en queue de file d'attente sera égal à 10:

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

Comme la capacité de notre file d'attente est de 10 et que nous avons ajouté 10 éléments, la file d'attente est saturée. L'ajout d'un nouvel élément entraîne la suppression du dernier élément de la file d'attente. Ajoutons unCustomClass avec levalue égal à -1:

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

Après cette action, le dernier élément de la file d'attente sera supprimé et le nouvel élément à la fin de celui-ci sera égal à 9. La nouvelle tête sera -1 car c'est le nouveau moindre élément selon lesComparator que nous avons passés lors de la construction de notre file d'attente:

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

Selon la spécification desMinMaxPriorityQueue,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.

Ajoutons un nombre 100 et testons si cet élément est dans la file d'attente après cette opération:

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

Comme nous le voyons, le premier élément de la file d'attente est toujours égal à -1 et le dernier est égal à 9. Par conséquent, l'ajout d'un entier a été ignoré car il est supérieur à l'élément déjà le plus important de la file d'attente.

4. Conclusion

Dans cet article, nous avons examiné les constructionsEvictingQueue etMinMaxPriorityQueue de la bibliothèque Guava.

Nous avons vu comment utiliser lesEvictingQueue comme tampon circulaire pour implémenter des programmes très efficaces.

Nous avons utilisé lesMinMaxPriorityQueue combinés auxComparator pour avoir l'accès en temps constant à son plus petit et plus grand élément.

Il est important de garder à l'esprit les caractéristiques des deux files d'attente présentées, car leur ajouter un nouvel élément remplacera un élément déjà présent dans la file d'attente. Ceci est contraire aux implémentations standard de la file d'attente, où l'ajout d'un nouvel élément à la file d'attente complète bloque le thread producteur ou déclenche une exception.

L'implémentation de tous ces exemples et extraits de code peut être trouvée dans leGitHub project - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.