【问题标题】:A fast queue in JavaJava中的快速队列
【发布时间】:2011-01-19 18:17:04
【问题描述】:

我正在寻找 Java 中的快速 queue 实现。我看到LinkedList 实现了Queue 接口,但它只会和LinkedList 一样快,对吧?有没有办法让队列更快,特别是对于add(我只需要polladd 并检查empty)。 下线我可能还需要PriorityQueue,但现在还不需要。

【问题讨论】:

  • 您是否验证过 LinkedList 不够快?在链表中添加应该很快...
  • LinkedList 不够快?您是否尝试过编写自己的代码来绕过 LinkedList 的瓶颈(无论它们是什么)?

标签: java queue


【解决方案1】:

如果多个线程要访问队列,请考虑使用ArrayBlockingQueue。否则请查看ArrayDeque。来自ArrayDeque API:

这个类可能比 堆栈时作为堆栈使用,速度更快 比 LinkedList 用作队列时。

如果现有数组有足够的容量,特别是基于数组的队列实现减少了调整底层数组大小的需要,因此通常比LinkedList更快地添加到队列中。请注意,ArrayBlockingQueue 是有界实现,而ArrayDeque 将根据需要调整大小。

另一方面,LinkedList 通常会提供更紧凑的表示,尤其是在您的队列大量增长和收缩的情况下。例如,如果您向 ArrayDeque 添加 10,000,000 个元素,然后删除 9,999,999 个元素,则底层数组的长度仍为 10,000,000,而 LinkedList 不会遇到此问题。

实际上,对于非阻塞队列的单线程访问,我倾向于使用LinkedList。我想性能差异是如此微不足道,您无论如何都不会注意到差异。

【讨论】:

  • LinkedBlockingQueue 实际上比 ArrayBlockingQueue 更快。因此,如果您使用的是阻塞的并发队列,请使用该队列。否则使用 ConcurrentLinkedQueue 比这两者都快。
  • @JohnVint,您是否有一些来源表明 ArrayBlockingQueue 比 LinkedBlockingQueue 慢? ArrayBlockingQueue 不应该是更快的,因为在对象的整个生命周期内支持数组从不调整大小?
  • @Adamski,当current_element_amt < 0.5 * capacity 时,ArrayDeque 不会在删除点自动缩小它的大小吗?
  • @Pacerier 我将不得不稍后查看它,但它在 Java Concurrency In Practice 一书中。它指出,因为它允许队列上的两个并行操作,所以它可以执行得更好。
  • 这应该是公认的答案。我对算法进行了基准测试,发现当我进行大约 3500 万次添加和删除时,ArrayDeque 比 LinkedList 快约 25%。
【解决方案2】:

我看到 LinkedList 实现了 Queue 接口,但它只会和 LinkedList 一样快吧?

目测源码,对于Queue.add、Queue.poll和Queue.peek操作,LinkedList是O(1)。

我希望这足够快。

【讨论】:

  • 对于链表数据结构来说,这将是一个非常愚蠢的成就。
  • 链接列表的搜索时间通常为 O(n),但如果您实际上将其用作队列,则它永远不会出现。
  • 没有什么能阻止单链表实现快速跳转到末尾。这是一个实现细节。
  • O(1) 并不意味着它很快。与其他 O(1) 方法相比,链表可能是 O(1),但仍然非常慢。
  • +1 给 Pacerier。对插入和删除 3500 万个条目的队列进行基准测试,ArrayDeque 比 LinkedList 快约 25%。
【解决方案3】:

如果链表的性能确实是个问题,另一种方法是在数组中实现“循环队列”,即随着条目的添加和删除,起点和终点移动的队列。如果您关心,我可以提供更多详细信息。当我使用没有集合库的语言时,我总是这样实现队列,因为它比链表更容易编写,而且速度更快。但是使用内置集合,为特殊情况编写和调试我自己的集合的努力在 99% 的时间里是不值得的:当它已经写好时,我可以用不同的方式编写它比我能更快的事实以 Java 的方式重写它几乎是一个无关紧要的事实。而且任何性能提升都可能太小,不值得麻烦。我对现有集合进行子类型化以获得我不时需要的特殊行为,但我很难想到上次我从头开始编写一个集合。

【讨论】:

  • 赞成,因为如果队列速度被描述为性能瓶颈,这是所选答案的一个很好的替代方案。
  • 您描述的内容已添加到 Java 6 中,称为 java.util.ArrayDeque。
【解决方案4】:

您可能想看看http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list,它介绍了GapList。这个新的列表实现结合了ArrayListLinkedList 的优势。

因此它实现了Deque 接口,但也可以像上面提到的ArrayDeque 那样预先设置大小。此外,您还可以免费获得List界面的所有可能。

【讨论】:

    【解决方案5】:

    以“类似 C/C++”的态度和固定大小的非常简单的旋转队列实现开始。

    class SimpleQueue<E>
    {
    
    int index   = 0;
    int head    = 0;
    int size    = 100;
    int counter = 0;
    E[] data    ;
    
    
    @SuppressWarnings("unchecked")
    SimpleQueue()
    {
        data = (E[]) new Object[size];
    }
    
    public void add(E e)
    {
        data[index]=e;
        index=(index+1)%size;
        counter++;
    }
    
    public E poll()
    {
        E value = data[head];
        head=(head+1)%size;
        counter--;
        return value;
    }
    
    public boolean empty()
    { return counter==0; }
    
    //Test
    public static void main(String[] args)
    {
        SimpleQueue<Integer> s = new SimpleQueue<Integer>();
    
        System.out.println(s.empty());
    
        for(int i=0; i< 10; i++)
            s.add(i);
    
        System.out.println(s.empty());
    
        for(int i=0; i<10; i++)
            System.out.print(s.poll()+",");
    
        System.out.println("\n"+s.empty());
    
    }
    }
    

    然后改进它。

    【讨论】:

    • ArrayDeque 是一个旋转队列。为什么要在 Java SE 6 库中重新发明轮子?
    猜你喜欢
    • 1970-01-01
    • 2015-03-12
    • 2019-02-22
    • 2013-04-09
    • 2016-08-27
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 2011-12-07
    相关资源
    最近更新 更多