web-dev-qa-db-fra.com

Existe-t-il une file d'attente de taille fixe qui supprime les éléments excessifs?

J'ai besoin d'une file d'attente avec une taille fixe. Lorsque j'ajoute un élément et que la file d'attente est saturée, l'élément le plus ancien doit être automatiquement supprimé.

Existe-t-il une implémentation pour cela en Java?

114
c0d3x

Il n'y a pas d'implémentation existante dans le Java Langage et exécution. Toutes les files d'attente s'étendent AbstractQueue , et sa documentation indique clairement que l'ajout d'un élément à une file d'attente complète se termine toujours par un Il serait préférable (et assez simple) d’envelopper une file d’attente dans une classe à part pour obtenir les fonctionnalités dont vous avez besoin.

Une fois encore, toutes les files d'attente étant des enfants de AbstractQueue, utilisez-le simplement comme type de données interne et vous devriez disposer d'une implémentation flexible s'exécutant pratiquement en un rien de temps :-)

MISE À JOUR:

Comme indiqué ci-dessous, il existe deux implémentations ouvertes disponibles (cette réponse est assez ancienne, bien sûr!), Voir cette réponse pour plus de détails.

17
moritz

En fait, le LinkedHashMap fait exactement ce que vous voulez. Vous devez remplacer la méthode removeEldestEntry.

Exemple pour une file d'attente avec 10 éléments maximum:

  queue = new LinkedHashMap<Integer, String>()
  {
     @Override
     protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
     {
        return this.size() > 10;   
     }
  };

Si "removeEldestEntry" renvoie la valeur true, l'entrée la plus ancienne est supprimée de la carte.

104
Mavrik

Oui, deux

De ma propre question dupliquée avec cette réponse correcte , j'ai appris de deux:

J'ai fait un usage productif de la goyave EvictingQueue, j'ai bien fonctionné.

60
Basil Bourque

Je viens d'implémenter une file d'attente de taille fixe de cette façon:

public class LimitedSizeQueue<K> extends ArrayList<K> {

    private int maxSize;

    public LimitedSizeQueue(int size){
        this.maxSize = size;
    }

    public boolean add(K k){
        boolean r = super.add(k);
        if (size() > maxSize){
            removeRange(0, size() - maxSize);
        }
        return r;
    }

    public K getYoungest() {
        return get(size() - 1);
    }

    public K getOldest() {
        return get(0);
    }
}
18
Roar Skullestad

C’est ce que j’ai fait avec Queue enveloppé avec LinkedList, c’est une taille fixe que je donne ici est 2;

public static Queue<String> pageQueue;

pageQueue = new LinkedList<String>(){
            private static final long serialVersionUID = -6707803882461262867L;

            public boolean add(String object) {
                boolean result;
                if(this.size() < 2)
                    result = super.add(object);
                else
                {
                    super.removeFirst();
                    result = super.add(object);
                }
                return result;
            }
        };


....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....
7
Berkay Turancı

Je pense que ce que vous décrivez est une file d'attente circulaire. Voici un exemple et voici un meilleur un

4
Sergey

Cette classe effectue le travail en utilisant la composition au lieu de l'héritage (d'autres réponses ici), ce qui supprime la possibilité de certains effets secondaires (comme le décrit Josh Bloch dans Essential Java). Le découpage de la LinkedList sous-jacente se produit sur les méthodes add, addAll et offer.

import Java.util.Collection;
import Java.util.Iterator;
import Java.util.LinkedList;
import Java.util.Queue;

public class LimitedQueue<T> implements Queue<T>, Iterable<T> {

    private final int limit;
    private final LinkedList<T> list = new LinkedList<T>();

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

    private boolean trim() {
        boolean changed = list.size() > limit;
        while (list.size() > limit) {
            list.remove();
        }
        return changed;
    }

    @Override
    public boolean add(T o) {
        boolean changed = list.add(o);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return list.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
        return list.iterator();
    }

    @Override
    public Object[] toArray() {
        return list.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return list.toArray(a);
    }

    @Override
    public boolean remove(Object o) {
        return list.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        boolean changed = list.addAll(c);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return list.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return list.retainAll(c);
    }

    @Override
    public void clear() {
        list.clear();
    }

    @Override
    public boolean offer(T e) {
        boolean changed = list.offer(e);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public T remove() {
        return list.remove();
    }

    @Override
    public T poll() {
        return list.poll();
    }

    @Override
    public T element() {
        return list.element();
    }

    @Override
    public T peek() {
        return list.peek();
    }
}
4
Dave Moten
public class CircularQueue<E> extends LinkedList<E> {
    private int capacity = 10;

    public CircularQueue(int capacity){
        this.capacity = capacity;
    }

    @Override
    public boolean add(E e) {
        if(size() >= capacity)
            removeFirst();
        return super.add(e);
    }
}

Usage et résultat du test:

public static void main(String[] args) {
    CircularQueue<String> queue = new CircularQueue<>(3);
    queue.add("a");
    queue.add("b");
    queue.add("c");
    System.out.println(queue.toString());   //[a, b, c]

    String first = queue.pollFirst();       //a
    System.out.println(queue.toString());   //[b,c]

    queue.add("d");
    queue.add("e");
    queue.add("f");
    System.out.println(queue.toString());   //[d, e, f]
}
2
Leon

Cela ressemble à une liste ordinaire où la méthode add contient un fragment supplémentaire qui tronque la liste si elle est trop longue.

Si cela est trop simple, vous devrez probablement modifier la description de votre problème.

0

Voir aussi this SO question , ou ArrayBlockingQueue (faites attention au blocage, cela pourrait être indésirable dans votre cas).

0
Zoran Regvart

On ne sait pas très bien quelles sont vos exigences qui vous ont amené à poser cette question. Si vous avez besoin d'une structure de données de taille fixe, vous pouvez également examiner différentes stratégies de mise en cache. Cependant, étant donné que vous avez une file d'attente, je suppose que vous recherchez un type de fonctionnalité de routeur. Dans ce cas, je choisirais un tampon en anneau: un tableau avec un premier et un dernier index. Chaque fois qu'un élément est ajouté, il vous suffit d'incrémenter le dernier index d'élément, et lorsqu'un élément est supprimé, d'incrémenter le premier index d'élément. Dans les deux cas, l'ajout est effectué modulo à la taille du tableau et veillez à incrémenter l'autre index le cas échéant, c'est-à-dire lorsque la file d'attente est pleine ou vide.

De même, s'il s'agit d'une application de type routeur, vous pouvez également essayer un algorithme tel que Random Early Dropping (RED) (Random Early Dropping (RED)), qui supprime des éléments de la file d'attente de manière aléatoire avant même qu'elle ne soit saturée. Dans certains cas, il a été constaté que les performances globales de RED étaient meilleures que celles de la méthode simple consistant à permettre à la file d’attente de se remplir avant de perdre.

0
JaakkoK

Une solution simple, ci-dessous est une file d'attente de "String"

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;

queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;

Notez que cela ne conservera pas l'ordre des éléments dans la file d'attente, mais remplacera l'entrée la plus ancienne.

0
M. Usman Khan

Je pense que la meilleure réponse est de cette autre question .

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.

0
Diego

En fait, vous pouvez écrire votre propre impl basé sur LinkedList, c'est assez simple, il suffit de remplacer la méthode add et de faire le staff.

0
Mike