Мне нужна очередь с фиксированным размером. Когда я добавляю элемент и очередь заполняется, он должен автоматически удалять самый старый элемент.
Есть ли существующая реализация для этого в Java?
Мне нужна очередь с фиксированным размером. Когда я добавляю элемент и очередь заполняется, он должен автоматически удалять самый старый элемент.
Есть ли существующая реализация для этого в Java?
В языке Java и среде исполнения не существует никакой реализации. Все очереди расширяются AbstractQueue, и в его документе четко указано, что добавление элемента в полную очередь всегда заканчивается исключением. Было бы лучше (и довольно просто) превратить очередь в собственный класс, чтобы получить необходимую вам функциональность.
Еще раз, потому что все очереди являются потомками AbstractQueue, просто используйте это как свой внутренний тип данных, и у вас должна быть гибкая реализация, работающая практически мгновенно :-)
UPDATE:
Как указано ниже, доступны две открытые реализации (этот ответ довольно старый, ребята!), Подробности смотрите в этом ответе.
Фактически LinkedHashMap делает именно то, что вы хотите. Вам нужно переопределить метод removeEldestEntry
.
Пример для очереди с максимальным количеством элементов:
queue = new LinkedHashMap<Integer, String>()
{
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
Если "removeEldestEntry" возвращает true, старшая запись удаляется с карты.
Из моего собственного дублирующего вопроса с этим правильным ответом я узнал о двух:
Я продуктивно использовал гуаву EvictingQueue
, работал хорошо.
Я только что реализовал очередь фиксированного размера следующим образом:
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);
}
}
Это то, что я сделал с Queue
, завернутым в LinkedList
. Это фиксированный размер, который я здесь даю, равен 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");
.....
Я думаю, что вы описываете круговую очередь. Вот пример и вот лучше one
Этот класс выполняет работу с использованием композиции вместо наследования (другие ответы здесь), которая устраняет возможность определенных побочных эффектов (как описано Джошем Блохом в Essential Java). Обрезка базового LinkedList происходит по методам add, addAll и предлагает.
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);
}
}
Использование и результат теста:
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]
}
Звучит как обычный список, в котором метод add содержит дополнительный фрагмент, который усекает список, если он слишком длинный.
Если это слишком просто, вам, вероятно, потребуется изменить описание проблемы.
Также см. этот вопрос SO или ArrayBlockingQueue (будьте осторожны о блокировке, это может быть нежелательным в вашем случае).
Не совсем понятно, какие у вас требования привели к тому, что вы задали этот вопрос. Если вам нужна структура данных фиксированного размера, вы также можете посмотреть различные политики кэширования. Однако, поскольку у вас есть очередь, я думаю, что вы ищете какой-то тип функций маршрутизатора. В этом случае я бы пошел с кольцевым буфером: массив с первым и последним индексом. Всякий раз, когда элемент добавляется, вы просто увеличиваете индекс последнего элемента, а когда элемент удаляется, увеличивайте индекс первого элемента. В обоих случаях добавление выполняется по модулю размера массива и при необходимости увеличивать другой индекс, т.е. Когда очередь заполнена или пуста.
Кроме того, если это приложение типа маршрутизатора, вы также можете поэкспериментировать с таким алгоритмом, как Random Early Dropping (RED), который случайно удаляет элементы из очереди даже до того, как он будет заполнен. В некоторых случаях было обнаружено, что RED имеет лучшую общую производительность, чем простой способ разрешить загрузке очереди до ее удаления.
На самом деле вы можете написать свой собственный impl на основе LinkedList, он довольно прямолинейный, просто переопределите метод добавления и сделайте команду.
Я думаю, что лучший ответ соответствует этому другому вопросу.
Наборы коллекций Apache 4 имеют CircularFifoQueue, что и есть то, что вы ищете. Цитирование javadoc:
CircularFifoQueue - это первая очередь в очереди с фиксированным размером, которая заменяет его самый старый элемент, если он заполнен.
Простое решение, ниже - очередь "String"
LinkedHashMap<Integer, String> queue;
int queueKeysCounter;
queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;
Обратите внимание, что это не будет поддерживать порядок элементов в очереди, но он заменит самую старую запись.