When working with collections in C#, it’s tempting to use a List for everything. After all, it’s flexible, efficient, and familiar.
But when your use case revolves around pushing and popping elements (Last-In, First-Out behavior), Stack is the better choice.
The Common Misuse of List<T>
Many developers implement stack-like behavior with a List :
var list = new List<int>();
// Push
list.Add(10);
list.Add(20);
// Peek
int value = list[^1];
// Pop
list.RemoveAt(list.Count - 1);
It works, but it’s not optimal:
You’re relying on RemoveAt, which isn’t semantically clear.
There’s more room for mistakes (like forgetting to use the last index).
List is optimized for random access, not stack-like behavior.
A better example if we have the following code
var value = list[^1];
list.RemoveAt(list.Count - 1);
If you accidentally do list.RemoveAt(0) , you just killed the wrong element (and it’s O(n) because it shifts the entire array).
The element at List[0] is discarded. But now, everything after it must shift left by one to fill the gap
Before: [ A, B, C, D ]
RemoveAt(0)
After: [ B, C, D, _ ]
The Right Tool Stack<T>
What is a stack?
A Stack in C# is a Last-In, First-Out (LIFO) collection.
Think of it like a stack of plates:
You add a plate on top (push)
You look at the top plate (Peek)
You remove the top plate (Pop)
The last item you put in is the first one that comes out.
var stack = new Stack<int>();
// Push
tack.Push(10);
stack.Push(20);
// Peek
int value = stack.Peek();
// Pop
stack.Pop();
Time Complexity of Operations
Push → O(1)
Adds an item on top of the stack.
Peek → O(1)
Reads the top item without removing it.
Pop → O(1)
Removes the top item.
All these are O(1) operations, unlike List<T> where removing at the wrong index can be O(n) because of shifting.