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


Answers:


19

Java语言和运行时中没有现有的实现。所有Queue都扩展了AbstractQueue,并且其文档明确指出,将元素添加到完整队列总是以异常结束。最好(而且很简单)将Queue包装到您自己的类中,以具有所需的功能。

再一次,由于所有队列都是AbstractQueue的子级,因此只需将其用作内部数据类型,您就应该有一个灵活的实现,它几乎可以在任何时间运行:-)

更新:

如下所述,有两种开放的实现方式(伙计们,这个答案很老!),有关详细信息,请参见此答案


4
使用Queue而不是AbstractQueue ...可能存在实现该接口但不扩展抽象类的队列。
TofuBeer

1
在Python中,您可以将collection.deque用作指定的maxlen
乔纳斯·格格(JonasGröger)2014年

2
更新现在有两个可用的此类。无需自己编写。在此页面上查看我的答案
罗勒·布尔克

107

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

最多包含10个元素的队列的示例:

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

如果“ removeEldestEntry”返回true,则将最旧的条目从地图中删除。


7
这实际上不执行队列操作,我如何检索最新消息。目的?
Alex

69

是的,两个

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


我有效地利用了番石榴EvictingQueue,效果很好。

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

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

...并且如果您不能使用Commons Collection 4.0,则CircularFifoBuffer似乎与3.0版中的CircularFifoQueue相似。
Sridhar Sarnobat


@ user7294900谢谢,链接固定。仅供参考,Stack Overflow邀请您直接对自己的“答复”进行此类编辑。任何人都可以编辑,而不仅仅是原始作者。在这方面,Stack Overflow旨在更像Wikipedia。
罗勒·布尔克

@BasilBourque是的,但是当我更改链接是灰色区域时,即使是我也可以拒绝此类编辑
user7294900

18

我只是这样实现了一个固定大小的队列:

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

1
应该是removeRange(0, size() - maxSize)
Ahmed Hegazy

@AhmedHegazy removeRange(0,size()-maxSize-1)是正确的
Ashish Doneriya

我同意上面的Amhed。删除-1。否则,在最大容量下,您将得到一个maxSize + 1的数组,因为我们正在谈论基于0的数组。例如。如果maxSize = 50,则在添加新对象时,原始帖子中的removeRange公式将为51-50-1 = 0(即未删除任何内容)。
Etep

8

这是我用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");
.....

4

我认为您所描述的是循环队列。下面是一个例子,这里是一个一个


4

此类使用组合而不是继承(此处提供其他答案)来完成工作,从而消除了某些副作用的可能性(如Essential Java中的Josh Bloch所述)。基本的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();
    }
}

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

0

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

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


实际上,他将需要删除第一个元素(即最早的元素),截断将删除最后一个元素。使用LinkedList仍然很实用。
MAK


0

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

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


0

实际上,您可以基于LinkedList编写自己的impl,这很简单,只需重写add方法并执行人员。


0

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

Apache commons collections 4具有您正在寻找的CircularFifoQueue。引用javadoc:

CircularFifoQueue是固定大小的先进先出队列,如果已满,将替换其最早的元素。


0

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

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;

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

请注意,这不会保持队列中项目的顺序,但是它将替换最早的条目。


0

正如在OOP中所建议的那样,我们应该更喜欢“ 组合”而不是“继承”

在这里,我的解决方案牢记这一点。

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();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.