在C#的集合体系中,Stack和Queue都属于非线性存储的集合类型,但两者的数据存储和取出规则存在本质差异,理解这些差异能帮助开发者在合适的场景下选择正确的集合类型。

核心定义与存储规则差异
Stack也就是栈,遵循后进先出(LIFO)的存储规则,最后存入的元素会被最先取出,类似生活中叠盘子的场景,最后放上去的盘子会被第一个拿下来。
Queue也就是队列,遵循先进先出(FIFO)的存储规则,最先存入的元素会被最先取出,类似生活中排队的场景,先到的人会先办理业务。
常用方法对比
两者都提供了基础的增删查操作,但方法名称和逻辑有明显区别,具体对比如下:
| 操作类型 | Stack对应方法 | Queue对应方法 | 方法说明 |
|---|---|---|---|
| 添加元素 | Push(T item) | Enqueue(T item) | 向集合中添加新元素 |
| 取出元素 | Pop() | Dequeue() | 移除并返回集合中的特定元素 |
| 查看顶部/头部元素 | Peek() | Peek() | 返回集合中的特定元素但不移除 |
| 判断元素是否存在 | Contains(T item) | Contains(T item) | 检查集合中是否包含指定元素 |
| 清空集合 | Clear() | Clear() | 移除集合中的所有元素 |
代码示例对比
Stack操作示例
以下代码展示Stack的基础操作流程:
using System;
using System.Collections.Generic;
class StackDemo
{
static void Main()
{
// 创建栈实例
Stack<string> stack = new Stack<string>();
// 入栈操作
stack.Push("第一个元素");
stack.Push("第二个元素");
stack.Push("第三个元素");
// 查看栈顶元素,不移除
Console.WriteLine("栈顶元素:" + stack.Peek()); // 输出:第三个元素
// 出栈操作,移除并返回栈顶元素
string popItem = stack.Pop();
Console.WriteLine("出栈元素:" + popItem); // 输出:第三个元素
// 遍历栈内剩余元素
Console.WriteLine("剩余元素:");
foreach (string item in stack)
{
Console.WriteLine(item);
}
// 输出顺序:第二个元素、第一个元素
}
}
Queue操作示例
以下代码展示Queue的基础操作流程:
using System;
using System.Collections.Generic;
class QueueDemo
{
static void Main()
{
// 创建队列实例
Queue<string> queue = new Queue<string>();
// 入队操作
queue.Enqueue("第一个元素");
queue.Enqueue("第二个元素");
queue.Enqueue("第三个元素");
// 查看队头元素,不移除
Console.WriteLine("队头元素:" + queue.Peek()); // 输出:第一个元素
// 出队操作,移除并返回队头元素
string dequeueItem = queue.Dequeue();
Console.WriteLine("出队元素:" + dequeueItem); // 输出:第一个元素
// 遍历队列内剩余元素
Console.WriteLine("剩余元素:");
foreach (string item in queue)
{
Console.WriteLine(item);
}
// 输出顺序:第二个元素、第三个元素
}
}
适用场景差异
根据两者的存储规则,适用场景也有明显区分:
- Stack适合需要后进先出逻辑的场景,比如方法调用栈的实现、撤销操作的记录、括号匹配校验等。
- Queue适合需要先进先出逻辑的场景,比如任务调度队列、消息队列的简易实现、广度优先搜索的辅助存储等。
其他注意点
两者都是非线程安全的集合类型,如果在多线程场景下使用,需要手动加锁或者使用线程安全版本的ConcurrentStack<T>和ConcurrentQueue<T>。另外,Stack和Queue的底层实现都是基于数组的动态扩容,频繁添加大量元素时会有一定的性能开销,在性能敏感的场景下需要注意初始化时指定合适的容量。