【发布时间】:2010-12-30 03:30:18
【问题描述】:
我需要一个固定大小的队列。当我添加一个元素并且队列已满时,它应该会自动删除最旧的元素。
在 Java 中是否有现成的实现?
【问题讨论】:
我需要一个固定大小的队列。当我添加一个元素并且队列已满时,它应该会自动删除最旧的元素。
在 Java 中是否有现成的实现?
【问题讨论】:
好的,我也会扔掉我的版本。 :-) 这是为了非常高效而构建的——在重要的时候。它不是基于 LinkedList - 并且是线程安全的(至少应该是)。先进先出
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
}
}
【讨论】:
从my own duplicate question和this correct answer,我了解到两个:
我有效地使用了 Guava EvictingQueue,效果很好。
要实例化 EvictingQueue,请调用静态工厂方法 create 并指定您的最大大小。
EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ; // Set maximum size to 100.
【讨论】:
CircularFifoQueue 链接已失效,请改用commons.apache.org/proper/commons-collections/apidocs/org/…
正如 OOP 中建议的那样,我们应该更喜欢 Composition over Inheritance
这是我的解决方案,请记住这一点。
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();
}
}
【讨论】:
我只是这样实现了一个固定大小的队列:
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);
}
}
【讨论】:
removeRange(0, size() - maxSize)
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]
}
【讨论】:
一个简单的解决方案,下面是一个“字符串”队列
LinkedHashMap<Integer, String> queue;
int queueKeysCounter;
queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;
请注意,这不会维护队列中项目的顺序,但会替换最旧的条目。
【讨论】:
Java 语言和运行时中没有现有的实现。所有队列都扩展AbstractQueue,其文档明确指出,将元素添加到完整队列总是以异常结束。最好(并且非常简单)将 Queue 包装到您自己的类中以获得您需要的功能。
再一次,因为所有队列都是 AbstractQueue 的子级,只需将其用作您的内部数据类型,您应该可以立即运行灵活的实现 :-)
更新:
如下所述,有两个可用的开放实现(这个答案已经很老了,伙计们!),有关详细信息,请参阅 this answer。
【讨论】:
collection.deque 和指定的 maxlen。
实际上LinkedHashMap 完全符合您的要求。您需要覆盖removeEldestEntry 方法。
最多包含 10 个元素的队列示例:
queue = new LinkedHashMap<Integer, String>()
{
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
如果“removeEldestEntry”返回 true,则从地图中删除最旧的条目。
【讨论】:
这是我用 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");
.....
【讨论】:
我认为最匹配的答案来自this other question。
Apache commons collections 4 有一个CircularFifoQueue,这就是您要查找的内容。引用 javadoc:
CircularFifoQueue 是一个具有固定大小的先进先出队列,如果已满则替换其最旧的元素。
【讨论】:
这个类使用组合而不是继承(这里的其他答案)来完成这项工作,这消除了某些副作用的可能性(正如 Josh Bloch 在 Essential Java 中所涵盖的那样)。底层 LinkedList 的修剪发生在方法 add、addAll 和 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();
}
}
【讨论】:
其实你可以基于LinkedList编写自己的impl,很简单,直接重写add方法,做staff。
【讨论】:
目前还不清楚您有什么要求导致您提出这个问题。如果您需要固定大小的数据结构,您可能还需要查看不同的缓存策略。但是,由于您有一个队列,我最好的猜测是您正在寻找某种类型的路由器功能。在这种情况下,我会使用环形缓冲区:一个具有第一个和最后一个索引的数组。每当添加一个元素时,您只需增加最后一个元素的索引,当删除一个元素时,增加第一个元素的索引。在这两种情况下,加法都是以数组大小为模执行的,并确保在需要时增加另一个索引,即当队列已满或为空时。
此外,如果它是路由器类型的应用程序,您可能还想尝试使用随机早期丢弃 (RED) 等算法,该算法甚至在队列被填满之前就从队列中随机丢弃元素。在某些情况下,已发现 RED 的整体性能优于让队列先填满再丢弃的简单方法。
【讨论】:
另请参阅 this SO question 或 ArrayBlockingQueue(请注意阻止,您的情况可能不需要这样做)。
【讨论】:
听起来像一个普通的列表,其中 add 方法包含一个额外的 sn-p,如果列表太长,它会截断列表。
如果这太简单了,那么您可能需要编辑您的问题描述。
【讨论】: