【问题标题】:Which Queue implementation to use in Java?在 Java 中使用哪个队列实现?
【发布时间】:2010-04-17 17:16:21
【问题描述】:

我需要在我的应用程序中使用 FIFO 结构。它最多需要有5个元素。 我想要一些易于使用的东西(我不关心并发),它实现了 Collection 接口。

我已经尝试过似乎来自队列的 LinkedList,但它似乎不允许我设置它的最大容量。感觉好像我只想要最多 5 个元素但尝试添加 20 个,它只会不断增加大小以适应它。我想要一些可以通过以下方式工作的东西:

XQueue<Integer> queue = new XQueue<Integer>(5); //where 5 is the maximum number of elements I want in my queue.
for (int i = 0; i < 10; ++i) {
    queue.offer(i);
}

for (int i = 0; i < 5; ++i) {
    System.out.println(queue.poll());
}

打印出来的:

5
6
7
8
9

谢谢

【问题讨论】:

  • 为什么要打印 5,6,..?为什么不是 0,1,2,3,4?报价似乎表明当达到最大容量时它会失败
  • 从技术上讲,我实际上对这种队列的需求是什么感兴趣,通常会忽略数据/任务(也就是说,由于某些平凡的原因将它们丢弃)不是什么好事。除此之外,这听起来像是我想尝试编码只是为了看看我是否可以让一个工作。
  • 似乎感兴趣的是跟踪前 5 个“最近使用”的元素。由于您只关心最近的,因此您需要丢弃旧的。

标签: java queue


【解决方案1】:

创建您自己的子类,并覆盖 add 方法,以便它

  • 检查新对象是否适合,如果不适合则失败
  • 调用 super.add()

(和构造函数)。

如果你想让它在插入时阻塞,那就是另一回事了。

【讨论】:

  • 嗯,这个想法是使用框架附带的东西。
  • 这不是破坏了能够子类化的目的吗?如果框架没有对象,你可以做一个。
  • 好吧,也许框架有一个,子类化会破坏拥有如此丰富的框架的目的。
  • 我认为您的请求是一种非常具体的请求,因此按照 Thorbjørn 的建议进行子类化是显而易见的解决方案。这很容易,保持代码干净并且可以完美地满足您的需求。那为什么不这样做呢?!
【解决方案2】:

我没有在 API 中看到任何类似的限制。您可以通过使用匿名类功能更改 add 方法的行为来使用 ArrayList:

new ArrayList<Object>(){
    public boolean add(Object o){ /*...*/ }
}

【讨论】:

    【解决方案3】:

    看起来你想要的是一个有限大小的 FIFO 结构,当添加新项目时,它会驱逐最旧的项目。我推荐一个基于 循环数组 实现的解决方案,您应该在其中跟踪队列尾和队列头的索引,并根据需要(以循环方式)增加它们。

    编辑: 这是我的实现(注意它是一个集合)。它适用于您的测试场景。

    public class XQueue <T> extends AbstractQueue<T>{
        private T[] arr;
        private int headPos;
        private int tailPos;
        private int size;
    
        @SuppressWarnings("unchecked")
        public XQueue(int n){
            arr = (T[]) new Object[n];
        }
    
        private int nextPos(int pos){
            return (pos + 1) % arr.length;
        }
    
        @Override
        public T peek() {
            if (size == 0)
                return null;
            return arr[headPos];
        }
    
        public T poll(){
            if (size == 0)
                return null;
            size--;
            T res = arr[headPos];
            headPos = nextPos(headPos);     
            return res;
        }
    
        @Override
        public boolean offer(T e) {
            if (size < arr.length)
                size++;
            else
                if (headPos == tailPos)
                    headPos = nextPos(headPos);
            arr[tailPos] = e;
            tailPos = nextPos(tailPos);
            return true;
        }
    
        @Override
        public Iterator<T> iterator() {
            return null; //TODO: Implement
        }
    
        @Override
        public int size() {
            return size;
        }
    }
    

    【讨论】:

      【解决方案4】:

      也许 ArrayBlockingQueue 可以解决问题。看here。试试这样的:

      BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5);
      
      for (int i = 0; i < 10; i++) {
        while (!queue.offer(i)) {
          queue.poll();        
        }
      }
      
      for (int i = 0; i < 5; i++) {   
        System.out.println(queue.poll());
      }
      

      【讨论】:

      • 问题是它装满后不允许我添加新项目。我希望它丢弃旧的。
      • 这就是 while 的用途。如果报价返回 false,则它无法添加元素,因此轮询队列以从头部删除一个元素。 while 的下一次迭代获得了接受该元素的提议,因为之前的轮询中有可用空间。
      【解决方案5】:

      你有三个选择

      1) Subclass an Abstract Collection

      2) 将大小限制为 5,并围绕要插入的代码执行逻辑。

      3) 使用LinkedListHashMap removeEldestEntry(Map.Entry) 方法可以在向映射添加新映射时强制执行自动删除陈旧映射的策略。 (然后您将使用迭代器来获取值 - 将按插入顺序返回)

      您最好的选择是#1 - 如果您查看链接,这真的很容易。

      【讨论】:

        【解决方案6】:

        您看过Apache Commons Collections 库吗? BoundedFifoBuffer 应该完全符合您的需求。

        【讨论】:

          【解决方案7】:

          如果我没记错的话,我已经使用 LinkedList 完成了您想要的操作。

          你需要做的是检查List的大小,如果它是5并且你想添加对象,只需删除第一个元素,如果大小是5就继续这样做。

          【讨论】:

            猜你喜欢
            • 2010-11-21
            • 1970-01-01
            • 2013-11-02
            • 1970-01-01
            • 2020-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多