Cola de tamaño limitado que contiene los últimos N elementos en Java


Una pregunta muy simple y rápida sobre las bibliotecas Java: ¿hay una clase ya hecha que implementa un Queue con un tamaño máximo fijo, es decir, siempre permite la adición de elementos, pero eliminará silenciosamente los elementos head para acomodar el espacio para los elementos recién agregados?

Por supuesto, es trivial implementarlo manualmente:

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

Por lo que veo, no hay una implementación estándar en Java stdlibs, pero puede haber una en Apache Commons o algo así.

Author: GreyCat, 2011-03-31

8 answers

Apache commons collections 4 tiene un CircularFifoQueue que es lo que está buscando. Citando el javadoc:

CircularFifoQueue es una cola first-in first-out con un tamaño fijo que reemplaza su elemento más antiguo si está lleno.

    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 está utilizando una versión anterior de las colecciones de Apache commons (3.x), puedes usar el CircularFifoBuffer que es básicamente lo mismo sin genéricos.

Update: respuesta actualizada tras el lanzamiento de la versión 4 de commons collections que soporta genéricos.

 135
Author: Asaf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-28 21:39:20

La guayaba ahora tiene un desahucio, una cola sin bloqueo que desaloja automáticamente elementos de la cabecera de la cola al intentar agregar nuevos elementos a la cola y está llena.

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]
 77
Author: Miguel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-16 08:48:20

Me gusta la solución @FractalizeR. Pero yo, además, mantener y devolver el valor 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;
    }
}
 9
Author: RenaudBlue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-14 16:28:42

Use composition not extends (sí quiero decir extends, como en una referencia a la palabra clave extends en java y sí esto es herencia). La composición es superior porque protege completamente su implementación, lo que le permite cambiar la implementación sin afectar a los usuarios de su clase.

Recomiendo probar algo como esto (estoy escribiendo directamente en esta ventana, para que el comprador tenga cuidado con los errores de sintaxis):

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

Una mejor opción (basada en la respuesta de Asaf) podría ser envuelva las colecciones de Apache CircularFifoBuffer con una clase genérica. Por ejemplo:

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
}
 4
Author: DwB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-15 14:04:16

Lo único que sé que tiene espacio limitado es la interfaz BlockingQueue (que por ejemplo está implementada por la clase ArrayBlockingQueue) - pero no eliminan el primer elemento si se llena, sino que bloquean la operación put hasta que el espacio esté libre (eliminado por otro hilo).

Que yo sepa, su implementación trivial es la forma más fácil de obtener tal comportamiento.

 3
Author: flolo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-12 15:05:54

Un LRUMap es otra posibilidad, también desde Apache Commons.

Http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LRUMap.html

 3
Author: rich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-22 08:26:57

Puedes usar un MinMaxPriorityQueue de Google Guava , del javadoc:

Se puede configurar una cola de prioridad min-max con un tamaño máximo. Si es así, cada vez que el tamaño de la cola excede ese valor, la cola elimina automáticamente su elemento más grande de acuerdo con su comparador (que podría ser el elemento que se acaba de agregar). Esto es diferente de las colas acotadas convencionales, que bloquean o rechazan nuevos elementos cuando están llenos.

 2
Author: moritz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-12 15:34:54
    public class ArrayLimitedQueue<E> extends ArrayDeque<E> {

    private int limit;

    public ArrayLimitedQueue(int limit) {
        super(limit + 1);
        this.limit = limit;
    }

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

    @Override
    public void addLast(E e) {
        super.addLast(e);
        while (size() > limit) {
            super.removeLast();
        }
    }

    @Override
    public boolean offerLast(E e) {
        boolean added = super.offerLast(e);
        while (added && size() > limit) {
            super.pollLast();
        }
        return added;
    }
}
 -2
Author: user590444,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-17 15:59:44