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?
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.
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.
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é.
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);
}
}
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");
.....
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();
}
}
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]
}
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.
Voir aussi this SO question , ou ArrayBlockingQueue (faites attention au blocage, cela pourrait être indésirable dans votre cas).
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.
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.
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.
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.