Picture the stack of clean plates in a canteen. The washer-up puts each plate on top of the pile, and the next person takes the plate off the top. Nobody ever slides a plate out of the middle — you'd send the lot crashing. So the plate you take is always the one that went on most recently, and the plate at the very bottom (the first one put down) is the last one anybody will ever touch.
That single rule — last in, first out, written LIFO — is the whole idea of a stack. A stack is a collection that only lets you add and remove at one end, called the top. It sounds like a restriction, and it is; but that restriction is exactly what makes stacks so useful for "most recent first" jobs like undo, the back button, and the way a program remembers where it was.
Because you may only touch the top, a stack has a tiny, tidy set of operations. Every stack you ever meet — in any language, in any exam board's pseudocode — offers these:
push(item) — put a new item on top of the stack;pop() — remove the top item and hand it back to you;peek() (also called top) — look at
the top item without removing it;isEmpty() — ask whether there is anything on the stack at all.Notice what is not on the list: there is no "get the third item", no "remove from the bottom", no searching through the middle. The only item a stack will ever show you or give back is the one on top. Watch the pile grow and shrink as items are pushed and popped:
A stack is easy to build on top of an push is the
array's .push (add to the end), and pop is the array's .pop
(remove from the end). Wrapping it in a class hides the array away and exposes only the
four safe operations — you literally cannot reach into the middle, because the array is
private.
The <T> just means "a stack of something" — a
Stack<string> holds strings, a Stack<number> holds numbers.
The logic is identical whatever T is.
Here is the moment that makes "last in, first out" click. We push three plates in order — red, then green, then blue — and then pop them off one at a time. Predict the order they come back before you press Run:
They come out blue, green, red — the exact reverse of the order they went in. That reversal is the signature of a stack: the last thing in is the first thing out. (In fact, pushing a sequence onto a stack and popping it all off again is a one-line way to reverse a list.)
The LIFO rule matches a surprising number of everyday computing jobs — always the ones where you care about the most recent thing first:
A classic exam use is checking that brackets match. Read left to right: push every opening bracket; on a closing bracket, pop the top and check it's the matching partner. If the stack is empty at the end and nothing ever mismatched, the brackets are balanced:
See how naturally the stack captures "the most recently opened bracket must be the first one closed" — that is LIFO.
A stack's twin is the queue. A queue is a line at a bus stop: you join at the back and you're served from the front, so it's first in, first out (FIFO) — fair, in-order service. A stack is the opposite: last in, first out. Use a stack when the newest thing matters most (undo, back button, recursion); use a queue when things should be handled in the order they arrived (a print queue, tasks waiting their turn). Same idea of "add at one end, remove at an end" — but different ends.
The classic stack bug is stack underflow — trying to pop or
peek when the stack is empty. There is no top item to hand back, so in
TypeScript pop() quietly returns undefined, which then
poisons whatever you do with it:
Always check isEmpty() first:
if (!s.isEmpty()) { const x = s.pop(); … }. In lower-level languages an underflow can
crash the program outright, so this habit matters everywhere.
The second trap is forgetting the golden rule: you may only touch the top. It is tempting to think "I'll just grab the item second from the top" — but a stack offers no such operation. If you find yourself wanting to reach into the middle, a stack is the wrong data structure for the job; you probably want an array or a different structure instead.