Menu

C# Stack: Push, Pop, Peek, Undo and Bracket Matching

Stack<T> is a last in, first out collection: the item added most recently comes out first. Learn Push, Pop and Peek, the empty stack exception and TryPop, why a stack enumerates in reverse, and two classic uses: an undo history and checking balanced brackets.

This page includes runnable editors - edit, run, and see output instantly.

A Stack<T> is a pile: you put items on top with Push and take them off the top with Pop, so the last item in is the first one out (LIFO). Only the top is reachable, and every operation on it takes constant time.

Push, Pop and Peek

Output:

On top: green
Count: 3
Took green
On top: red
Took red
Took blue
Count: 0

green was pushed last, so it comes off first. Peek returns the top without changing the stack, which is how you inspect what Pop would give you before deciding to take it.

The empty stack exception and TryPop

Popping or peeking at an empty stack throws InvalidOperationException. This shows up most often in parsers and algorithms fed with input that has more closing items than opening ones.

Output:

Caught InvalidOperationException
True 10
False 0

TryPop and TryPeek (.NET Core 2.0 and later) return false on an empty stack and set the out variable to the default value, 0 here. On .NET Framework, check Count > 0 first.

Iteration order: top first

Enumerating a stack does not remove anything, and it goes from the top down, in the order Pop would return the items:

Output:

checkout products home 
checkout > products > home
True
home
checkout

The reversed copy catches people out: the constructor takes any IEnumerable<T> and pushes its items in order, and a stack enumerates top first, so the old top ends up at the bottom of the copy. Reversing the sequence first (LINQ's Reverse() returns the items bottom first) gives a copy with the same top.

Pushing a list of items onto a new stack also reverses them, which is a quick way to reverse a sequence: new Stack<char>("hello") pops back o, l, l, e, h.

Example: an undo history

Editors keep every change on a stack. Undo pops the most recent change and reverts it; redo keeps a second stack of undone changes.

Output:

Hello, world!
Hello, world
Hello
Hello, world

Storing whole snapshots is the simplest version. Real editors push small command objects instead (what was inserted, and where), each with a method to reverse itself, but the two stacks work the same way.

Example: balanced brackets

Checking that (, [ and { are closed in the right order is the standard stack exercise, and the same logic sits inside every compiler and JSON parser.

Output:

"f(a[i], {x: 1})" -> True
"(]" -> False
"((a)" -> False
"a)b(" -> False
"" -> True

The three failure checks correspond to the three ways brackets go wrong: a closer with nothing open (a)b(, caught by Count == 0 instead of an exception from Pop), a closer of the wrong kind ((]), and openers never closed (((a), caught by the final check).

Other uses

  • Depth first search. Replace the queue in a breadth first search with a stack and the traversal goes deep before wide. An explicit stack also replaces recursion when the input is deep enough to risk a StackOverflowException, which cannot be caught.
  • Evaluating expressions. Postfix notation (3 4 + 2 *) is evaluated by pushing numbers and popping two for each operator.
  • Backtracking. Navigation history, maze solving and parser states push a position and pop back to it on a dead end.

See Queue for the first in, first out counterpart.

Stack vs Queue vs List

Stack<T>Queue<T>List<T>
Order outNewest firstOldest firstAny, by index
AddPushEnqueueAdd, Insert
RemovePop (top)Dequeue (front)Remove, RemoveAt
LookPeekPeeklist[i]
Safe variantsTryPop, TryPeekTryDequeue, TryPeeknot needed

For several threads, ConcurrentStack<T> in System.Collections.Concurrent offers Push, TryPop and TryPeek without locks.

Common mistakes

  • Popping without checking. An empty stack throws InvalidOperationException; check Count or use TryPop.
  • Expecting foreach to go from the first item pushed. It goes from the top.
  • Copying with new Stack<T>(stack). The copy is reversed.
  • Pushing inside foreach over the same stack. Throws; use a while (stack.Count > 0) loop.

Frequently Asked Questions

What is a Stack in C#?

Stack<T> in System.Collections.Generic is a last in, first out (LIFO) collection. Push puts an item on top, Pop removes and returns the top item, and Peek returns the top item without removing it. All three run in constant time.

What happens when you Pop an empty stack in C#?

Pop and Peek throw InvalidOperationException when the stack is empty. Check stack.Count > 0 first, or use TryPop(out var item) and TryPeek(out var item), which return false instead of throwing (.NET Core 2.0 and later).

In what order does foreach go through a Stack?

From the top down: the most recently pushed item comes first, the same order Pop would return them. ToArray() uses the same order. A consequence is that new Stack<T>(otherStack) produces a reversed copy, because the constructor pushes the items in the order it enumerates them.

What is the difference between a Stack and a Queue in C#?

A Stack<T> returns the newest item first (last in, first out), while a Queue<T> returns the oldest item first (first in, first out). Use a stack for undo history, nested structures and depth first search; use a queue for processing work in arrival order and breadth first search.

How do I check for balanced brackets in C#?

Scan the string once. Push every opening bracket onto a Stack<char>. For every closing bracket, the stack must be non-empty and its top must be the matching opening bracket, which you then pop. The string is balanced when the scan ends with the stack empty.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED