Ограниченная по размеру очередь, содержащая последние N элементов в Java
Очень простой и быстрый вопрос о библиотеках Java: есть ли готовый класс, который реализует Queue
с фиксированным максимальным размером - то есть он всегда позволяет добавлять элементы, но он будет молча удалять элементы головы для размещения пространства для вновь добавленных элементов.
Конечно, тривиально реализовать его вручную:
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;
}
}
Насколько я вижу, стандартная реализация в Java stdlib отсутствует, но может быть там в Apache Commons или что-то в этом роде?
Ответы
Ответ 1
Коллекции коллекций Apache 4 имеют CircularFifoQueue < > , что и есть то, что вы ищете. Цитирование javadoc:
CircularFifoQueue - это первая очередь в очереди с фиксированным размером, которая заменяет его самый старый элемент, если он заполнен.
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]
Если вы используете более старую версию коллекций коллекций Apache (3.x), вы можете использовать CircularFifoBuffer, что в основном одно и то же без дженериков.
Обновление: обновленный ответ после выпуска коллекций коллекций версии 4, который поддерживает дженерики.
Ответ 2
В Guava теперь есть EvictingQueue, неблокирующая очередь, которая автоматически вытесняет элементы из головы очереди при попытке добавить новую элементов в очередь и заполняется.
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]
Ответ 3
Мне нравится решение @FractalizeR. Но я бы добавил сохранить и вернуть значение из 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;
}
}
Ответ 4
Использовать состав не расширяет (да, я имею в виду extends, как в ссылке на ключевое слово extends в java и да, это наследование). Композиция более сложная, поскольку она полностью защищает вашу реализацию, позволяя вам изменять реализацию, не затрагивая пользователей вашего класса.
Я рекомендую попробовать что-то вроде этого (я набираю прямо в это окно, поэтому покупатель остерегается синтаксических ошибок):
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
Лучший вариант (основанный на ответе Asaf) может заключаться в том, чтобы обернуть Apache Collections CircularFifoBuffer с общим классом. Например:
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
Единственное, что я знаю, имеет ограниченное пространство - это интерфейс BlockingQueue (который, например, реализуется классом ArrayBlockingQueue), но он не удаляет первый элемент, если он заполнен, а вместо этого блокирует операцию put, пока свободное пространство не будет удалено (удалено другой нитью).
Насколько я знаю, ваша тривиальная реализация - самый простой способ получить такое поведение.
Ответ 6
Вы можете использовать MinMaxPriorityQueue из Google Guava, из javadoc:
Очередь приоритетов min-max может быть настроена с максимальным размером. Если это так, каждый раз, когда размер очереди превышает это значение, очередь автоматически удаляет свой наибольший элемент в соответствии со своим компаратором (который может быть только что добавленным элементом). Это отличается от обычных ограниченных очередей, которые либо блокируют, либо отклоняют новые элементы при заполнении.
Ответ 7
LRUMap - это еще одна возможность, также из Apache Commons.
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LRUMap.html
Ответ 8
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;
}
}