是否有固定大小的队列来删除多余的元素?

我需要一个固定大小的队列。当我添加一个元素并且队列已满时,它应该自动删除最老的元素。

在 Java 中是否存在这样的实现?

95285 次浏览

听起来像是一个普通的 List,其中 add 方法包含一个额外的代码片段,如果列表太长,这个代码片段将截断列表。

如果这太简单,那么您可能需要编辑您的问题描述。

Java 语言和运行时中没有现有的实现。所有队列都扩展了 抽象队列,其文档清楚地表明,向完整队列添加元素总是以异常结束。最好(而且非常简单)将 Queue 封装到您自己的类中,以获得所需的功能。

同样,因为所有队列都是 AbstractQueue 的子队列,所以只需将其用作内部数据类型,您就可以在几乎没有时间的情况下运行一个灵活的实现: -)

更新:

如下所述,有两个可用的开放实现(这个答案很老了,伙计们!) ,详情请参阅 这个答案

也请参阅 这个所以问题ArrayBlockingQueue(小心阻塞,这在您的情况下可能是不需要的)。

我想你描述的是一个循环队列,这是一个 例子,这是一个 好多了

不太清楚是什么要求导致您提出这个问题。如果需要固定大小的数据结构,可能还需要查看不同的缓存策略。但是,由于您有一个队列,我最好的猜测是您正在寻找某种类型的路由器功能。在这种情况下,我将使用一个环形缓冲区: 一个具有第一个和最后一个索引的数组。每当添加一个元素时,只需递增最后一个元素索引,当删除一个元素时,递增第一个元素索引。在这两种情况下,对数组大小进行加法,并确保在需要时增加其他索引,即队列已满或为空时。

此外,如果它是一个路由器类型的应用程序,您可能还想尝试一种算法,如随机早期丢弃(RED) ,这种算法甚至在队列被填满之前就从队列中随机丢弃元素。在某些情况下,RED 被发现比允许队列在下降之前填满的简单方法具有更好的整体性能。

实际上,LinkedHashMap可以完全满足您的需要,您需要重写 removeEldestEntry方法。

最大10个元素的队列示例:

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

如果“ RemoveEldestEntry”返回 true,则从映射中删除最年长的条目。

实际上,你可以基于 LinkedList 编写自己的 impl,它非常简单,只需覆盖 add 方法并执行 staff。

这个类使用组合而不是继承来完成这项工作(其他答案在这里) ,它消除了某些副作用的可能性(正如 Josh Bloch 在必要 Java 中提到的那样)。在 add、 addAll 和 offer 方法上会修剪基础 LinkedList。

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

是的,二号

我自己的重复问题正确答案中,我学到了两点:


我充分利用了番石榴 EvictingQueue,工作得很好。

要实例化 EvictingQueue,请调用静态工厂方法 create并指定最大大小。

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

我认为最匹配的答案来自 另一个问题

Apachecommons 集合4有一个 循环五队,这正是你要寻找的:

CircularFIFoQueue 是一个先进先出的队列,具有固定的大小,如果已满,则替换其最老的元素。

这是我用 LinkedList包装的 Queue,它是固定大小的,我在这里给出的是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");
.....

一个简单的解决方案,下面是一个“ String”队列

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;


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

请注意,这将不会维护 Queue 中项的 Order,但是它将替换最早的条目。

public class CircularQueue<E> extends LinkedList<E> {
private final int capacity;


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

正如在面向对象的建议,我们应该更喜欢 继承的组合

这就是我的解决方案。

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

好吧,我把我的版本也扔了。* 这是建设非常性能-当这一点很重要的时候。它不是基于 LinkedList-而且是线程安全的(至少应该是)。FIFO

static class FixedSizeCircularReference<T> {
T[] entries


FixedSizeCircularReference(int size) {
this.entries = new Object[size] as T[]
this.size = size
}
int cur = 0
int size


synchronized void add(T entry) {
entries[cur++] = entry
if (cur >= size) {
cur = 0
}
}


List<T> asList() {
int c = cur
int s = size
T[] e = entries.collect() as T[]
List<T> list = new ArrayList<>()
int oldest = (c == s - 1) ? 0 : c
for (int i = 0; i < e.length; i++) {
def entry = e[oldest + i < s ? oldest + i : oldest + i - s]
if (entry) list.add(entry)
}
return list
}
}