Stacks and queues
Table of contents
Introduction
In your prior programming experience, we focused primarily on control abstraction, such as by introducing methods to help us build Java programs.
In CSE 143, we’re interested in the idea of data abstraction and, in particular, the study of abstract data types (ADTs). We’ve seen two examples of ADTs and several implementations for those ADTs.
- List
- In Java, there are two common implementations of the
List
interface:ArrayList
andLinkedList
. - Set
- In Java, there are two common implementations of the
Set
interface:HashSet
andTreeSet
.
In this lesson, we’ll introduce two new ADTs called stacks and queues. Stacks and queues are very simple structures: in fact, a list can do everything that a stack can do and also everything that queue can do.
Why might we want to use or implement simpler ADTs?
In our introduction to sets, we saw how sets could be used to speed up text processing. Since lists associate elements with indices, ArrayList.contains
needs to loop over potentially all of the elements in the list. Sets, on the other hand, don’t have this restriction so the HashSet
implementation can be optimized for contains
. This is the same idea for stacks and queues: by supporting fewer operations, our data structures can optimize for those operations.
Stacks
The stack abstract data type that allows adding and removing elements from the “top” of the stack. Imagine a stack of plates. The most common of way of interacting with the stack of plates is to add a plate to the top or remove a plate from the top.
push
an element to the top of the stack.pop
to remove and return the top element from the stack.peek
to return but not remove the top element from the stack.size
to return the number of elements in the stack.isEmpty
to return true if and only if the stack is empty.
Consider the following stack.
Client
Stack<String> stack = new Stack<String>();
stack.push("puppy");
stack.push("cat");
stack.push("dog");
System.out.println(stack.pop());
The first element removed by stack.pop()
is “dog” since “dog” was at the top of the stack.
Which element will be removed by the next pop: "puppy" or "cat"?
The next element removed by stack.pop()
will be “cat” since “cat” is on top of “puppy” in the stack.
There’s one weird detail about stacks in Java. Computer scientists consider the idea of a stack as an ADT, not an implementation. Like ArrayList
and LinkedList
, there are different ways of implementing stacks. Unfortunately, Java’s Stack
class has a critical design flaw: it’s a class, not an interface. This means that the left hand side and right hand side of an assignment statement will both be declared with the Stack
type, rather than using the interface type that we learned in the previous lesson.
- Note
- The Java
Stack
class has many other methods beyondpush
,pop
,peek
,size
, andisEmpty
, but they’re off-limits for this course since they don’t represent the idea of a stack. (Poor design!)
Because stacks add to the top and remove from the top, we consider them last-in, first-out (LIFO) structures. In other words, the most recently-pushed element is the element that will be the first to be removed.
Queues
If stacks are last-in, first-out (LIFO), queues are first-in, first-out (FIFO). Whereas a stack only makes changes to the top, a queue makes changes to the “front” and the “back” of the queue. Imagine people waiting in the checkout line at the grocery store. When someone wants to checkout, they’re added to the back of the queue (which might be empty). The first person to checkout is the person at the front of the line.
add
an element to the back of the queue.remove
and return the element at the front of the queue.peek
to return but not remove the element at the front of the queue.size
to return the number of elements in the stack.isEmpty
to return true if and only if the stack is empty.
Consider the following queue.
Client
Queue<String> queue = new LinkedList<String>();
queue.add("puppy");
queue.add("cat");
queue.add("dog");
System.out.println(queue.remove());
The first element removed by queue.remove()
is “puppy” since “puppy” was at the front of the queue.
This time, Java got the design right for queues. Queue
is an interface in Java, which means that we can’t instantiate it directly. Instead, we need to use an implementation such as LinkedList
—the same LinkedList
that also implements the List
interface.
This is the powerful idea behind ADTs. By declaring an object such as a LinkedList
as the interface type Queue
, we’re signalling to the human reading the program that we intend to use this object in queue-like ways, rather than all of the possible ways that we might use a list.