容纳Java的最后N个元素的大小限制队列

Gre*_*Cat 185 java queue collections

关于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;
    }
}
Run Code Online (Sandbox Code Playgroud)

据我所知,Java stdlibs中没有标准的实现,但可能是Apache Commons中的那个或类似的东西?

Asa*_*saf 160

Apache commons集合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]
Run Code Online (Sandbox Code Playgroud)

如果您使用的是较旧版本的Apache commons集合(3.x),则可以使用CircularFifoBuffer,它基本上是相同的,没有泛型.

更新:在支持泛型的commons集合版本4发布后更新的答案.

  • 请参阅[这个其他答案](http://stackoverflow.com/a/15156403/642706),链接到2013年10月左右添加到*Google Guava*15版的"EvictingQueue". (3认同)

小智 87

番石榴现在有一个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]
Run Code Online (Sandbox Code Playgroud)

  • 现在这是正确的答案。从文档中还不清楚,但是EvictingQueue是FIFO。 (2认同)

Ren*_*aud 11

我喜欢@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 一旦有人调用 `add(int,E)` 就会中断。`addAll` 是否按预期工作,取决于未指定的实现细节。这就是为什么你应该更喜欢委托而不是继承...... (7认同)
  • @KonradMorawski无论如何整个LinkedList类都不是线程安全的,所以你的评论在这种情况下毫无意义! (6认同)
  • 应该指出的是,这种解决方案不是线程安全的 (3认同)
  • @RenaudBlue 线程安全是一个有效的关注点(如果经常被忽视),所以我认为该评论没有意义。并提醒“LinkedList”不是线程安全的也不是没有意义的。在这个问题的上下文中,OP的具体要求使得添加项目作为原子操作执行变得尤为重要。换句话说,不确保原子性的风险会比常规 LinkedList 的风险更大。 (2认同)

flo*_*olo 5

我唯一知道空间有限的是 BlockingQueue 接口(例如由 ArrayBlockingQueue 类实现) - 但如果填充,它们不会删除第一个元素,而是阻止放置操作,直到空间空闲(由其他线程删除) )。

据我所知,您的微不足道的实现是获得这种行为的最简单方法。


DwB*_*DwB 5

使用组合不扩展(是的,我的意思是扩展,如在java中对extends关键字的引用,是的,这是继承).组合更加超级,因为它完全屏蔽了您的实现,允许您更改实现而不会影响您的类的用户.

我建议尝试这样的东西(我直接在这个窗口中输入,所以买家要注意语法错误):

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
Run Code Online (Sandbox Code Playgroud)

一个更好的选择(基于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
}
Run Code Online (Sandbox Code Playgroud)

  • +1*如果*你解释为什么作文是一个更好的选择(除了"更喜欢作品而不是继承)..."并且有一个很好的理由 (2认同)
  • 组合对于我的任务来说是一个糟糕的选择:它意味着至少是对象数量的两倍 =&gt; 垃圾收集的频率至少是两倍。我使用了大量(数千万)这些大小有限的队列,例如:Map&lt;Long, LimitedSizeQueue&lt;String&gt;&gt;。 (2认同)