【问题标题】:Is there a fixed sized queue which removes excessive elements?是否有一个固定大小的队列可以删除过多的元素?
【发布时间】:2010-12-30 03:30:18
【问题描述】:

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

在 Java 中是否有现成的实现?

【问题讨论】:

标签: java queue


【解决方案1】:

好的,我也会扔掉我的版本。 :-) 这是为了非常高效而构建的——在重要的时候。它不是基于 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
    }
}

【讨论】:

    【解决方案2】:

    是的,两个

    my own duplicate questionthis correct answer,我了解到两个:


    我有效地使用了 Guava EvictingQueue,效果很好。

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

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

    【讨论】:

    • ...如果您不能使用 Commons Collection 4.0,那么 CircularFifoBuffer 似乎类似于 v 3.0 中的 CircularFifoQueue。
    • CircularFifoQueue 链接已失效,请改用commons.apache.org/proper/commons-collections/apidocs/org/…
    • @user7294900 谢谢,链接已修复。仅供参考,Stack Overflow 邀请您自己直接对答案进行此类编辑。任何人都可以编辑,而不仅仅是原作者。 Stack Overflow 旨在在这方面更像维基百科。
    • @BasilBourque 是的,但是即使我在更改链接时也可以拒绝此类编辑,这是一个 灰色 区域
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      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)
      • @AhmedHegazy removeRange(0, size() - maxSize - 1) 是正确的
      • 我同意上面的 Amhed。删除 - 1。否则在最大容量下,您最终会得到一个 maxSize + 1 的数组,因为我们谈论的是基于 0 的。例如。如果 maxSize = 50,那么在添加新对象时,原始帖子中的 removeRange 公式将为 51 - 50 - 1 = 0(即没有被删除)。
      【解决方案5】:
      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]
      }
      

      【讨论】:

        【解决方案6】:

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

        LinkedHashMap<Integer, String> queue;
        int queueKeysCounter;
        
        queue.put(queueKeysCounter++, "My String");
        queueKeysCounter %= QUEUE_SIZE;
        

        请注意,这不会维护队列中项目的顺序,但会替换最旧的条目。

        【讨论】:

          【解决方案7】:

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

          再一次,因为所有队列都是 AbstractQueue 的子级,只需将其用作您的内部数据类型,您应该可以立即运行灵活的实现 :-)

          更新:

          如下所述,有两个可用的开放实现(这个答案已经很老了,伙计们!),有关详细信息,请参阅 this answer

          【讨论】:

          • 使用队列而不是AbstractQueue...可能有队列实现了接口但不扩展抽象类。
          • 在 Python 中,您可以使用 collection.deque 和指定的 maxlen
          • UPDATE 现在有两个这样的类可用。无需自己编写。请参阅此页面上的my answer
          【解决方案8】:

          实际上LinkedHashMap 完全符合您的要求。您需要覆盖removeEldestEntry 方法。

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

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

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

          【讨论】:

          • 这实际上并没有做队列所做的事情,我怎样才能检索最新的。对象?
          • 获取 values() 中的最后一项。
          【解决方案9】:

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

          【讨论】:

            【解决方案10】:

            我认为最匹配的答案来自this other question

            Apache commons collections 4 有一个CircularFifoQueue,这就是您要查找的内容。引用 javadoc:

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

            【讨论】:

              【解决方案11】:

              这个类使用组合而不是继承(这里的其他答案)来完成这项工作,这消除了某些副作用的可能性(正如 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();
                  }
              }
              

              【讨论】:

                【解决方案12】:

                其实你可以基于LinkedList编写自己的impl,很简单,直接重写add方法,做staff。

                【讨论】:

                  【解决方案13】:

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

                  此外,如果它是路由器类型的应用程序,您可能还想尝试使用随机早期丢弃 (RED) 等算法,该算法甚至在队列被填满之前就从队列中随机丢弃元素。在某些情况下,已发现 RED 的整体性能优于让队列先填满再丢弃的简单方法。

                  【讨论】:

                    【解决方案14】:

                    我认为您所描述的是一个循环队列。这是example,这是better一个

                    【讨论】:

                      【解决方案15】:

                      另请参阅 this SO questionArrayBlockingQueue(请注意阻止,您的情况可能不需要这样做)。

                      【讨论】:

                        【解决方案16】:

                        听起来像一个普通的列表,其中 add 方法包含一个额外的 sn-p,如果列表太长,它会截断列表。

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

                        【讨论】:

                        • 其实他需要删除第一个元素(即最早的),截断会删除最后一个元素。使用 LinkedList 仍然很实用。
                        猜你喜欢
                        • 2018-03-03
                        • 2011-12-04
                        • 2012-05-28
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-07-05
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多