【发布时间】:2009-07-14 03:27:06
【问题描述】:
在Java中,队列的一种实现是“循环数组”,另一种是“链表”。它们有什么区别?
【问题讨论】:
-
更好的提问方式是“有什么不同”
-
呵呵——是的,这个问题的绝对“正确”答案是“是的。有区别。”
标签: java queue collections
在Java中,队列的一种实现是“循环数组”,另一种是“链表”。它们有什么区别?
【问题讨论】:
标签: java queue collections
就它们的使用方式而言,几乎没有区别,除非您达到队列的大小限制(我将在稍后解释...)
至于其他考虑:
链表方法是有利的,因为您可以动态调整队列大小而无需额外的努力 - 这是链表的基础。当然,这可以通过重新分配数组来复制,但这并不是最简单的方法。
另外,在链表中,没有未使用的内存:在循环数组的方法中,如果队列的大小小于数组的最大容量,就会出现“空槽”。然而,这并不意味着链表更节省内存,因为:
循环数组方法的优点是没有开销。链表中的每个节点都需要存储对下一个节点的引用——如果列表变大,这会累加。另一方面,循环数组只是一块内存,您可以通过索引访问它,因此没有开销。
每种方法都有优点和缺点,但实际上,这取决于它所使用的具体情况......除非你一直使用队列,否则它可能不会有太大的不同。
【讨论】:
通常,循环数组实现在重用内存方面稍好一些,但如果将太多项目添加到队列中,则存在不得不增长的风险 - 如果正常存储和最大存储容量太大,可能会占用太多内存实际情况不同。
链表更灵活,但通常涉及更多的垃圾回收。
在实践中,如果您发现您的代码瓶颈取决于此选择,我想我会感到惊讶 - 使用您认为最直观的选择。
【讨论】:
与 ArrayList 和 LinkedList 之间的区别大致相同。
对于数组,你需要很好地估计队列可以有多大,因为你需要为它分配存储空间。但是这样做之后,当接近容量时,它会更加紧凑。 “空闲点”仍然占用数组中的空间,这在 LinkedList 中没有。
对于链表,从中间删除和添加元素更容易(尽管队列根本不需要这样做)。
该数组是随机访问的,这意味着您可以快速访问位置 x 处的元素。同样,此功能在队列中毫无用处。
【讨论】:
作为链表实现的队列没有固定大小,而作为循环数组实现的队列,又名ring buffer,通常具有固定大小(尽管可以制作一个调整大小的队列,非常ArrayList 调整大小的方式)。
链表实现每个元素使用更多内存,但数组实现需要更多连续内存。这两个问题实际上只是一个重大问题,因为元素的数量变得非常大。
在循环数组实现中添加/删除元素非常便宜,因为它只涉及调整计数器和设置引用,而链表实现在添加时必须分配元素,并且在删除时会产生 GC 开销。
【讨论】:
编写您的代码,使其仅使用接口,创建队列除外。然后很容易切换实现。
选择一个实现作为开始。我通常使用数组变体(例如 ArrayList),因为它们更小并且在当今的计算机上往往更快,我认为这是由于缓存(我只是做了一个小基准测试,通过 10000 个元素队列推送 10000000 个元素,~ ArrayBlockingQueue 8.3s,LinkedBlockingQueue 10-11s)。如果我需要索引访问,我也会使用数组变体。只有在列表或队列中间有很多插入/删除操作时,我才会选择链表变体。
如果您遇到性能问题并且分析显示队列是瓶颈(这不太可能),请对队列的两种实现进行基准测试并选择更好的一种。
【讨论】: