在C#的System.Collections.Generic命名空间下,Stack<T>是一个泛型栈结构,遵循后进先出的数据存储规则,适合处理需要反向访问数据、临时存储中间结果等场景。

Stack栈的基础概念
栈的核心特性是后进先出,也就是最后存入栈中的元素,会最先被取出。和队列的先进先出特性形成对比,栈的操作仅允许在栈顶进行,不支持随机访问中间元素。
Stack<T>是泛型版本,指定存储的元素类型后可以避免装箱拆箱操作,性能比非泛型Stack更优,实际开发中优先使用泛型版本。
Stack栈的常用操作方法
1. 初始化栈
使用new关键字可以创建指定类型的Stack实例,也可以在初始化时传入集合批量添加元素。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 创建空的整数栈
Stack<int> intStack = new Stack<int>();
// 创建字符串栈并初始化元素
Stack<string> strStack = new Stack<string>(new[] { "a", "b", "c" });
Console.WriteLine($"字符串栈初始元素数量:{strStack.Count}");
}
}
2. 入栈操作Push
Push方法用于将元素添加到栈的顶部,每次调用都会让栈的元素数量加1。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack<int> stack = new Stack<int>();
// 依次入栈三个元素
stack.Push(10);
stack.Push(20);
stack.Push(30);
Console.WriteLine($"入栈后栈元素数量:{stack.Count}"); // 输出3
}
}
3. 出栈操作Pop
Pop方法会移除并返回栈顶的元素,如果栈为空时调用Pop会抛出InvalidOperationException异常,使用前建议先判断栈是否为空。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack<int> stack = new Stack<int>();
stack.Push(10);
stack.Push(20);
// 出栈获取栈顶元素
int topElement = stack.Pop();
Console.WriteLine($"出栈的元素是:{topElement}"); // 输出20
Console.WriteLine($"出栈后栈元素数量:{stack.Count}"); // 输出1
}
}
4. 查看栈顶元素Peek
Peek方法会返回栈顶的元素,但不会将其从栈中移除,同样在栈为空时调用会抛出异常,需要提前判断。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack<string> stack = new Stack<string>();
stack.Push("first");
stack.Push("second");
// 查看栈顶元素不移除
string peekElement = stack.Peek();
Console.WriteLine($"栈顶元素是:{peekElement}"); // 输出second
Console.WriteLine($"查看后栈元素数量:{stack.Count}"); // 输出2
}
}
5. 判断栈是否为空
通过Count属性可以判断栈中是否有元素,Count为0时表示栈为空,也可以直接用Any()方法(需要引入System.Linq命名空间)判断。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
Stack<int> stack = new Stack<int>();
Console.WriteLine($"栈是否为空(Count判断):{stack.Count == 0}"); // 输出True
Console.WriteLine($"栈是否为空(Any判断):{stack.Any() == false}"); // 输出True
stack.Push(5);
Console.WriteLine($"入栈后栈是否为空:{stack.Count == 0}"); // 输出False
}
}
6. 遍历栈元素
栈支持foreach遍历,但遍历顺序是从栈顶到栈底,也就是和入栈顺序相反。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
Console.WriteLine("遍历栈元素:");
foreach (int item in stack)
{
Console.WriteLine(item);
}
// 输出顺序为3、2、1
}
}
Stack栈的适用场景
- 方法调用栈:程序运行时的方法调用顺序就是用栈来维护的,后调用的方法先执行完返回。
- 表达式求值:处理算术表达式、括号匹配等场景时,栈可以临时存储运算符和中间结果。
- 撤销操作实现:很多软件的撤销功能,就是把操作记录存入栈,撤销时从栈顶取出最近的操作反向执行。
- 深度优先搜索:在图或树的深度优先遍历中,栈可以用来存储待访问的节点。
注意事项
首先,Stack不是线程安全的,如果需要在多线程场景下使用,建议使用ConcurrentStack<T>,或者自己加锁处理。
其次,Pop和Peek方法在栈为空时都会抛出异常,实际开发中如果不确定栈是否为空,可以先判断Count属性,或者使用TryPop、TryPeek方法,这两个方法不会抛出异常,会在操作成功时返回true,失败时返回false。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack<int> stack = new Stack<int>();
// 尝试出栈,栈为空时不会抛异常
if (stack.TryPop(out int result))
{
Console.WriteLine($"出栈成功,元素:{result}");
}
else
{
Console.WriteLine("栈为空,无法出栈");
}
}
}