C# Stack and Queue — push, pop, enqueue, dequeue, and when to use each
Stack<T> and Queue<T> are two fundamental collection types in C# that impose a specific access order. Use them when the order you add items must dictate the order you remove them.
- Stack — Last In, First Out (LIFO). Think of a stack of plates: you add and remove from the top.
- Queue — First In, First Out (FIFO). Think of a checkout line: first to arrive is first to leave.
Stack<T> — LIFO
Basic Operations
C# Example Code
var stack = new Stack<int>();
// Push — add to the top
stack.Push(1);
stack.Push(2);
stack.Push(3);
Console.WriteLine($"Count: {stack.Count}"); // Count: 3
// Peek — look at the top without removing
Console.WriteLine($"Top: {stack.Peek()}"); // Top: 3
// Pop — remove and return the top item
int top = stack.Pop();
Console.WriteLine($"Popped: {top}"); // Popped: 3
Console.WriteLine($"Count: {stack.Count}"); // Count: 2
// Iterate — from top to bottom (does not remove items)
foreach (var item in stack)
Console.Write($"{item} ");
// Output: 2 1TryPop and TryPeek (safe versions)
C# Example Code
var stack = new Stack<string>();
stack.Push("first");
// Returns false and default if empty — no exception
if (stack.TryPop(out string? value))
Console.WriteLine($"Popped: {value}"); // Popped: first
bool hasPeek = stack.TryPeek(out string? top);
Console.WriteLine(hasPeek); // False — stack is emptyStack Example — Undo History
C# Example Code
var history = new Stack<string>();
// User performs actions
history.Push("Typed 'Hello'");
history.Push("Bold applied");
history.Push("Font changed to Arial");
Console.WriteLine("Action log (most recent first):");
foreach (var action in history)
Console.WriteLine($" {action}");
// Undo last action
string? undone = history.TryPop(out var last) ? last : null;
Console.WriteLine($"\nUndone: {undone}"); // Undone: Font changed to Arial
Console.WriteLine($"Remaining: {history.Count}"); // Remaining: 2Stack Example — Balanced Parentheses Checker
C# Example Code
bool IsBalanced(string expression)
{
var stack = new Stack<char>();
foreach (char c in expression)
{
if (c is '(' or '[' or '{')
{
stack.Push(c);
}
else if (c is ')' or ']' or '}')
{
if (stack.Count == 0) return false;
char open = stack.Pop();
if ((c == ')' && open != '(') ||
(c == ']' && open != '[') ||
(c == '}' && open != '{'))
return false;
}
}
return stack.Count == 0;
}
Console.WriteLine(IsBalanced("({[hello]})")); // True
Console.WriteLine(IsBalanced("({[hello]}")); // False — missing closing
Console.WriteLine(IsBalanced("({[hello])}")); // False — wrong orderQueue<T> — FIFO
Basic Operations
C# Example Code
var queue = new Queue<string>();
// Enqueue — add to the back
queue.Enqueue("Alice");
queue.Enqueue("Bob");
queue.Enqueue("Carol");
Console.WriteLine($"Count: {queue.Count}"); // Count: 3
// Peek — look at the front without removing
Console.WriteLine($"Next: {queue.Peek()}"); // Next: Alice
// Dequeue — remove and return the front item
string first = queue.Dequeue();
Console.WriteLine($"Served: {first}"); // Served: Alice
Console.WriteLine($"Count: {queue.Count}"); // Count: 2
// Iterate — front to back (does not remove)
foreach (var item in queue)
Console.Write($"{item} ");
// Output: Bob CarolTryDequeue and TryPeek
C# Example Code
var queue = new Queue<int>();
queue.Enqueue(10);
if (queue.TryDequeue(out int value))
Console.WriteLine($"Dequeued: {value}"); // Dequeued: 10
bool hasNext = queue.TryPeek(out int front);
Console.WriteLine(hasNext); // False — queue is emptyQueue Example — Task Processor
C# Example Code
var taskQueue = new Queue<string>();
// Producer adds tasks
taskQueue.Enqueue("Send email to Alice");
taskQueue.Enqueue("Generate report");
taskQueue.Enqueue("Backup database");
// Consumer processes tasks in order
while (taskQueue.TryDequeue(out string? task))
{
Console.WriteLine($"Processing: {task}");
}
// Output:
// Processing: Send email to Alice
// Processing: Generate report
// Processing: Backup databasePriorityQueue<TElement, TPriority> (.NET 6+)
PriorityQueue<T, P> dequeues items in order of priority (lowest priority number first).
C# Example Code
var pq = new PriorityQueue<string, int>();
pq.Enqueue("Low priority task", 3);
pq.Enqueue("Critical bug fix", 1);
pq.Enqueue("Feature request", 2);
pq.Enqueue("Another critical fix", 1);
while (pq.TryDequeue(out string? task, out int priority))
Console.WriteLine($"[P{priority}] {task}");
// [P1] Critical bug fix
// [P1] Another critical fix
// [P2] Feature request
// [P3] Low priority taskStack vs Queue — Quick Comparison
Stack<T> | Queue<T> | |
|---|---|---|
| Order | LIFO — last in, first out | FIFO — first in, first out |
| Add | Push() | Enqueue() |
| Remove | Pop() | Dequeue() |
| Look without removing | Peek() | Peek() |
| Safe operations | TryPop(), TryPeek() | TryDequeue(), TryPeek() |
| Common use cases | Undo history, parsing, DFS | Task queues, BFS, request buffering |
| Thread-safe alternative | ConcurrentStack<T> | ConcurrentQueue<T> |
Performance
Both Stack<T> and Queue<T> provide O(1) push/pop/enqueue/dequeue operations. Internal resizing (like List<T>) is O(n) amortized but rare.
C# Example Code
// All four core operations are O(1):
var s = new Stack<int>();
s.Push(1); // O(1)
s.Pop(); // O(1)
var q = new Queue<int>();
q.Enqueue(1); // O(1)
q.Dequeue(); // O(1)Thread-Safe Alternatives
For multi-threaded scenarios, use the concurrent variants from System.Collections.Concurrent.
C# Example Code
using System.Collections.Concurrent;
var safeStack = new ConcurrentStack<int>();
safeStack.Push(1);
safeStack.TryPop(out int _);
var safeQueue = new ConcurrentQueue<string>();
safeQueue.Enqueue("task");
safeQueue.TryDequeue(out string? _);