为什么.NET中队列的底层实现使用数组?

时间:2013-08-21 19:45:20

标签: c# .net data-structures queue

我正在阅读C#在一个Nutshell中,书中提到Queue数据结构的底层实现使用了一个根据需要调整大小的数组。这个调整大小当然会有成本,所以我想知道使用它的背后的理由是说双链表是什么?鉴于我们只关心第一个和最后一个元素,并且双链表比数组更有效地调整大小,为什么要使用数组呢?数组会占用更少的内存,但这是唯一的理由吗?

编辑: 对不起,刚才意识到这几乎与此完全重复: Why are Stack<T> and Queue<T> implemented with an array? (他们的问题甚至来自同一本书)。不管怎样,谢谢你的所有答案!

3 个答案:

答案 0 :(得分:5)

出于同样的原因,使用数组而不是链接列表实现了许多其他数据结构,如堆栈,哈希表,邻接列表等:

  • 使用事实上的标准调整大小策略,指数增长,即使您从一个空数组开始并插入几百万个项目,您也只需要二十几个调整大小操作。而前几个调整大小的操作只会复制几个字节。
  • 特别是队列很少无限制地增长,所以你大多可以避免调整大小。
  • 它不仅仅是更紧凑,一个双向链接的公共值类型的引用类型(int)比同等数组大三倍,甚至没有将每个对象的分配开销帐户。如果我们诚实并考虑到这些,我们必须添加一个或两个单词的每节点标题,因此它更像是4x或5x更大。这使阵列可能存在的任何过度分配相形见绌。
  • 由于连续性,阵列使各种缓存的使用大致无限好。这实际上影响了您可以对队列执行的任何操作,除了询问其大小,包括调整大小。
  • 必须分配所有这些列表节点并进行垃圾回收。虽然分代GC应该使分配非常便宜,并且应该很容易选择只在队列中花费很少时间的节点,但仍然需要做很多不必要的工作,如果队列很大或者很少使用,那么很多节点可以在较小的GC之前存活下来从队列的末尾弹出,所以就是这样。

答案 1 :(得分:2)

将数组用作循环缓冲区,就像它们所做的那样,对于可以假设具有最大大小的队列更好。数组的增长是使它更昂贵的部分,因此如果您假设队列大小不会不断扩展,或者在创建队列时提供了有效的最大值,则使用数组仅优于双链表。 the all-knowing Wikipedia上有一个关于循环缓冲区的详细解释(甚至特别提到了队列):

  

循环缓冲为具有固定最大大小的队列提供了良好的实施策略。如果队列采用最大大小,则循环缓冲区是完全理想的实现方式;所有队列操作都是恒定时间。然而,扩展循环缓冲器需要移位存储器,这是相对昂贵的。对于任意扩展队列,可能首选链接列表方法。

简而言之,因为大多数用例不会涉及任意扩展队列,并且因为对于那些关注性能的人来说,可以从一开始就提供最大值,使用了一个用作循环缓冲区的后备数组。

答案 2 :(得分:0)

恕我直言,双链表在概念上很漂亮。但理论上,它要求为每个节点分配一个隔离的内存块,这是非常昂贵的。我相信任何严格的双链表实现应该实际使用预先分配的内存块,根据需要按部分扩展/缩小。这就是为什么数组实际上是实现队列的理想后端。