System.Collections.Generic
中的 List<T>
能够完成 Stack<T>
所能完成的所有功能,并且还有更多扩展功能 -- 它们是基于相同的底层数据结构。在什么情况下选择使用 Stack<T>
是正确的呢?
System.Collections.Generic
中的 List<T>
能够完成 Stack<T>
所能完成的所有功能,并且还有更多扩展功能 -- 它们是基于相同的底层数据结构。在什么情况下选择使用 Stack<T>
是正确的呢?
Stack<T>
声明了你希望代码被使用的方式。计划未来总是好的,但如果你现在需要 Stack<T>
,并且没有使用 List<T>
的强烈理由,那么我会选择 Stack<T>
。Stack<T>
可以表明你希望这段代码如何被使用。虽然未来的计划总是好的,但如果你现在需要 Stack<T>
,并且没有充分的理由使用 List<T>
,那么我会选择 Stack<T>
。 - Abe MiesslerList
列表在逻辑上是一个Stack
,并尝试在末尾以外的位置添加/删除/读取时会发生什么?此外,如果您在逻辑上表示堆栈,则以后不需要随机访问,这就是重点。这种情况并不罕见;Stack
是一种有用的逻辑数据结构。 - ServyStack.Pop()
,但这会使代码更复杂。 另外,对于以后阅读该代码的人来说,这看起来可能有点奇怪,而且不够简洁。 - Abe MiesslerStack
。当然,你真正想要这样做的时候是有限的,但在适当的时候它是一个重要的工具。
例如,假设正在处理的数据如果不执行堆栈顺序就毫无意义,在这些情况下,如果你将数据作为列表提供,那么你将会自找麻烦。通过使用Stack
(或Queue
,或任何其他有序敏感的结构),你可以在代码中精确定义数据的使用方式。Queue<T>
与List<T>
和Stack<T>
非常不同。(我怀疑队列是双端队列实现,或者在实际队列周围加上填充实现摊销常数时间队列结构)但List<T>
和Stack<T>
是相同的数据结构,只是它们周围包裹的不同。这些是具体实现,而不是假设的list<t>
和stack<t>
接口。 - Billy ONealQueue
实现为循环数组(根据 MSDN 页面上的内容),而不是双端队列。遗憾的是,.NET 在 System.Collection
中没有双端队列实现。 - ServyStack
。如果你使用栈数据结构,它将在整个代码中传达程序员的意图,并防止不小心误用数据结构(无意中添加/删除/读取其他位置而不是一个端点)。Stack
可能只是一个接口,而不是具体实现。你可以让 List
实现这个接口。问题主要在于方便性。如果有人需要一个栈,他们需要选择一些具体的实现并记住(“哦,是的,List 是首选的栈实现”),而不仅仅是新建具体类型。这一切都与概念有关。列表是一个列表,栈是一个栈,它们执行两个非常不同的操作。它们唯一的共同点是它们的通用性和可变长度。
列表是一个可变长度的项目集合,其中任何元素都可以通过索引访问和重写,并且可以在任何此类索引处添加和删除项目。
堆栈是一个可变长度的元素集合,支持LIFO访问模型;只能访问堆栈顶部的元素,并且只能从该集合的“端点”添加和删除元素。从“顶部”三个元素的项目只能通过“弹出”上面的两个元素来显示它。
使用正确的工具来完成工作;当您需要对集合中的任何元素进行“随机”访问时,请使用列表。当您希望强制执行对数组中元素的更受限制的“仅顶部”访问时,请使用堆栈。当您想要强制执行FIFO“管道”时,请使用队列;项目从一端进入,从另一端出去。
Stack
中也有不同的方法可以使您的代码更清晰(且更易于生活),而与使用 List
时相应的代码相比,这些方法非常实用。Stack
时,一个简单的对象池代码片段:if (!pool.TryPop(out var obj))
{
obj = new Foo();
}
而且同样可以写成一个List
,同时保持操作O(1):
Foo obj;
int count = pool.Count;
if (count > 0)
{
obj = pool[--count];
pool.RemoveAt(count);
}
else
{
obj = new Foo();
}
Stack<T>
更清晰地传达了意图。 - CodesInChaosList
给你一个项目列表,而Stack
给你一个FILO(先进后出)队列。 - Dave Zych