web-dev-qa-db-fra.com

File d'attente limitée en taille qui contient les N derniers éléments de Java

Une question très simple et rapide sur les bibliothèques Java: existe-t-il une classe prête à l'emploi qui implémente un Queue avec une taille maximale fixe - c'est-à-dire qu'elle permet toujours l'ajout d'éléments, mais qu'elle sera supprimée silencieusement? éléments de tête pour accueillir de l'espace pour les éléments nouvellement ajoutés.

Bien sûr, il est trivial de l'implémenter manuellement:

import Java.util.LinkedList;

public class LimitedQueue<E> extends LinkedList<E> {
    private int limit;

    public LimitedQueue(int limit) {
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        super.add(o);
        while (size() > limit) { super.remove(); }
        return true;
    }
}

Autant que je sache, il n'y a pas d'implémentation standard dans Java stdlibs, mais peut-être en existe-t-il dans Apache Commons ou quelque chose du genre?

182
GreyCat

Apache commons collections 4 a un CircularFifoQueue <> qui correspond à ce que vous recherchez. Citant le javadoc:

CircularFifoQueue est une file d'attente premier entré, premier sorti avec une taille fixe qui remplace son élément le plus ancien si elle est pleine.

    import Java.util.Queue;
    import org.Apache.commons.collections4.queue.CircularFifoQueue;

    Queue<Integer> fifo = new CircularFifoQueue<Integer>(2);
    fifo.add(1);
    fifo.add(2);
    fifo.add(3);
    System.out.println(fifo);

    // Observe the result: 
    // [2, 3]

Si vous utilisez une version antérieure des collections Apache commons (3.x), vous pouvez utiliser le CircularFifoBuffer , qui est fondamentalement la même chose sans les génériques.

Update : réponse mise à jour après la publication de la version 4 de commons collections qui prend en charge les génériques.

156
Asaf

Guava a maintenant un EvictingQueue , une file d'attente non bloquante qui supprime automatiquement les éléments de l'en-tête de la file d'attente lorsque vous tentez d'ajouter de nouveaux éléments à la file d'attente et qu'elle est saturée. .

import Java.util.Queue;
import com.google.common.collect.EvictingQueue;

Queue<Integer> fifo = EvictingQueue.create(2); 
fifo.add(1); 
fifo.add(2); 
fifo.add(3); 
System.out.println(fifo); 

// Observe the result: 
// [2, 3]
85
Miguel

J'aime la solution @FractalizeR. Mais je voudrais en plus garder et retourner la valeur de super.add (o)!

public class LimitedQueue<E> extends LinkedList<E> {

    private int limit;

    public LimitedQueue(int limit) {
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        boolean added = super.add(o);
        while (added && size() > limit) {
           super.remove();
        }
        return added;
    }
}
11
Renaud

Utiliser la composition non étend (oui, je veux dire, comme dans une référence au mot-clé extend dans Java et oui, il s'agit d'un héritage). La composition est supérieure car elle protège complètement votre implémentation, vous permettant de la modifier sans affecter les utilisateurs de votre classe.

Je recommande d'essayer quelque chose comme ça (je tape directement dans cette fenêtre, donc méfiez-vous des erreurs de syntaxe):

public LimitedSizeQueue implements Queue
{
  private int maxSize;
  private LinkedList storageArea;

  public LimitedSizeQueue(final int maxSize)
  {
    this.maxSize = maxSize;
    storageArea = new LinkedList();
  }

  public boolean offer(ElementType element)
  {
    if (storageArea.size() < maxSize)
    {
      storageArea.addFirst(element);
    }
    else
    {
      ... remove last element;
      storageArea.addFirst(element);
    }
  }

  ... the rest of this class

Une meilleure option (basée sur la réponse de Asaf) pourrait consister à envelopper Apache Collections CircularFifoBuffer avec une classe générique. Par exemple:

public LimitedSizeQueue<ElementType> implements Queue<ElementType>
{
    private int maxSize;
    private CircularFifoBuffer storageArea;

    public LimitedSizeQueue(final int maxSize)
    {
        if (maxSize > 0)
        {
            this.maxSize = maxSize;
            storateArea = new CircularFifoBuffer(maxSize);
        }
        else
        {
            throw new IllegalArgumentException("blah blah blah");
        }
    }

    ... implement the Queue interface using the CircularFifoBuffer class
}
5
DwB

La seule chose que je sache qui a peu d’espace est l’interface BlockingQueue (qui est par exemple implémentée par la classe ArrayBlockingQueue). ).

À ma connaissance, votre implémentation triviale est le moyen le plus simple d’obtenir un tel comportement.

4
flolo

Une LRUMap est une autre possibilité, également d’Apache Commons.

http://commons.Apache.org/collections/apidocs/org/Apache/commons/collections/map/LRUMap.html

3
rich

Vous pouvez utiliser un MinMaxPriorityQueue à partir de Google Guava , à partir du javadoc:

Une file d'attente prioritaire min-max peut être configurée avec une taille maximale. Si tel est le cas, chaque fois que la taille de la file d'attente dépasse cette valeur, celle-ci supprime automatiquement son élément le plus important en fonction de son comparateur (qui peut être l'élément qui vient d'être ajouté). Cela diffère des files d'attente limitées conventionnelles, qui bloquent ou rejettent de nouveaux éléments lorsqu'elles sont pleines.

2
moritz