Esiste una coda di dimensioni fisse che rimuove elementi eccessivi?


127

Ho bisogno di una coda con dimensioni fisse. Quando aggiungo un elemento e la coda è piena, dovrebbe rimuovere automaticamente l'elemento più vecchio.

Esiste un'implementazione esistente per questo in Java?


Risposte:


19

Non esiste un'implementazione esistente in Java Language and Runtime. Tutte le code estendono AbstractQueue e il relativo documento afferma chiaramente che l'aggiunta di un elemento a una coda completa termina sempre con un'eccezione. Sarebbe meglio (e abbastanza semplice) avvolgere una coda in una classe tutta tua per avere le funzionalità di cui hai bisogno.

Ancora una volta, poiché tutte le code sono figli di AbstractQueue, utilizzale semplicemente come tipo di dati interno e dovresti avere un'implementazione flessibile in esecuzione praticamente in un batter d'occhio :-)

AGGIORNARE:

Come indicato di seguito, ci sono due implementazioni aperte disponibili (questa risposta è piuttosto vecchia, gente!), Vedere questa risposta per i dettagli.


4
Usa Queue invece di AbstractQueue ... potrebbero esserci code che implementano l'interfaccia ma non estendono la classe astratta.
TofuBeer,

1
In Python puoi usare a collection.dequecon uno specificato maxlen.
Jonas Gröger,

2
AGGIORNAMENTO Ora ci sono due di queste classi disponibili. Non c'è bisogno di scriverne uno tuo. Vedi la mia risposta in questa pagina.
Basil Bourque,

108

In realtà LinkedHashMap fa esattamente quello che vuoi. Devi sovrascrivere il removeEldestEntrymetodo.

Esempio per una coda con massimo 10 elementi:

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

Se "removeEldestEntry" restituisce true, la voce più vecchia viene rimossa dalla mappa.


7
questo in realtà non fa quello che fa una coda, come posso recuperare il più recente. oggetto?
Alex

69

Sì, due

Dalla mia domanda duplicata con questa risposta corretta , ho appreso di due:


Ho fatto un uso produttivo della Guava EvictingQueue , ha funzionato bene.

Per creare un'istanza di una EvictingQueuechiamata il metodo factory statico createe specificare la dimensione massima.

EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ;  // Set maximum size to 100. 

... e se non puoi usare Commons Collection 4.0, CircularFifoBuffer sembra essere simile a CircularFifoQueue in v 3.0.
Sridhar Sarnobat,

CircularFifoQueueil link è morto, usa invece commons.apache.org/proper/commons-collections/apidocs/org/…
user7294900

@ user7294900 Grazie, collegamento corretto. Cordiali saluti, Stack Overflow ti invita a apportare tali modifiche direttamente a una risposta tu stesso. Chiunque può modificare, non solo l'autore originale. Stack Overflow è destinato a più come Wikipedia in questo senso.
Basil Bourque,

@BasilBourque sì, ma tali modifiche possono essere respinti anche da me quando si cambia link si tratta di una grigia zona
user7294900

18

Ho appena implementato una coda di dimensioni fisse in questo modo:

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

1
Dovrebbe essereremoveRange(0, size() - maxSize)
Ahmed Hegazy il

@AhmedHegazy removeRange (0, size () - maxSize - 1) è corretto
Ashish Doneriya

Sono d'accordo con Amhed sopra. Rimuovere il - 1. Altrimenti alla massima capacità si finirà con un array che è maxSize + 1 poiché stiamo parlando di 0 in base. Per esempio. Se maxSize = 50, quando si aggiunge un nuovo oggetto la formula removeRange nel post originale sarà 51 - 50 - 1 = 0 (ovvero non è stato rimosso nulla).
Etep

8

Questo è quello che ho fatto con Queueavvolto LinkedList, è di dimensioni fisse che do qui è 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");
.....

4

Penso che quello che stai descrivendo sia una coda circolare. Ecco un esempio, ed ecco un meglio uno


4

Questa classe fa il lavoro usando la composizione invece dell'ereditarietà (altre risposte qui) che rimuove la possibilità di alcuni effetti collaterali (come coperto da Josh Bloch in Essential Java). Il taglio della LinkedList sottostante si verifica sui metodi add, addAll e 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();
    }
}

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

Utilizzo e risultati del 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]
}

0

Sembra un normale elenco in cui il metodo add contiene un frammento aggiuntivo che tronca l'elenco se diventa troppo lungo.

Se è troppo semplice, probabilmente dovrai modificare la descrizione del tuo problema.


In realtà, avrebbe bisogno di eliminare il primo elemento (cioè il primo), troncando rimuoverà l'ultimo elemento. Ancora pratico con una LinkedList.
MAK,


0

Non è del tutto chiaro quali siano i requisiti che ti hanno portato a porre questa domanda. Se hai bisogno di una struttura di dati di dimensioni fisse, potresti anche voler esaminare diversi criteri di memorizzazione nella cache. Tuttavia, poiché hai una coda, la mia ipotesi migliore è che stai cercando un tipo di funzionalità del router. In tal caso, andrei con un buffer ad anello: un array che ha un primo e l'ultimo indice. Ogni volta che viene aggiunto un elemento, è sufficiente incrementare l'ultimo indice dell'elemento e quando un elemento viene rimosso, incrementare il primo indice dell'elemento. In entrambi i casi, l'addizione viene eseguita in base alla dimensione dell'array e assicurarsi di incrementare l'altro indice quando necessario, ovvero quando la coda è piena o vuota.

Inoltre, se si tratta di un'applicazione di tipo router, potresti anche voler sperimentare un algoritmo come Random Early Dropping (RED), che elimina gli elementi dalla coda in modo casuale anche prima che si riempia. In alcuni casi, è stato riscontrato che RED ha prestazioni complessive migliori rispetto al semplice metodo per consentire il riempimento della coda prima di rilasciare.


0

In realtà puoi scrivere il tuo impianto basato su LinkedList, è abbastanza semplice, basta ignorare il metodo di aggiunta e fare lo staff.


0

Penso che la risposta migliore sia da questa altra domanda .

Le raccolte 4 di Apache commons hanno un CircularFifoQueue che è quello che stai cercando. Citando il javadoc:

CircularFifoQueue è una coda first-in first-out con una dimensione fissa che sostituisce il suo elemento più vecchio se pieno.


0

Una soluzione semplice, di seguito è una coda di "String"

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;

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

Si noti che ciò non manterrà l'ordine degli articoli nella coda, ma sostituirà la voce più vecchia.


0

Come è stato consigliato negli OOP, dovremmo preferire la composizione all'ereditarietà

Ecco la mia soluzione tenendo presente questo.

package com.choiceview;

import java.util.ArrayDeque;

class Ideone {
    public static void main(String[] args) {
        LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
        q.add(1);
        q.add(2);
        q.add(3);
        System.out.println(q);

        q.add(4);
        // First entry ie 1 got pushed out
        System.out.println(q);
    }
}

class LimitedArrayDeque<T> {

    private int maxSize;
    private ArrayDeque<T> queue;

    private LimitedArrayDeque() {

    }

    public LimitedArrayDeque(int maxSize) {
        this.maxSize = maxSize;
        queue = new ArrayDeque<T>(maxSize);
    }

    public void add(T t) {
        if (queue.size() == maxSize) {
            queue.removeFirst();
        }
        queue.add(t);
    }

    public boolean remove(T t) {
        return queue.remove(t);
    }

    public boolean contains(T t) {
        return queue.contains(t);
    }

    @Override
    public String toString() {
        return queue.toString();
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.