In this set of notes, we will talk about two new abstract data types: Stacks and Queues.
Before we get into those, however, let’s talk a little bit more about Abstract Data Types and a special type of Java class called an interface.
In a previous set of notes we discussed the concept of an Abstract Data Type (ADT). Recall that we defined it as follows:
An abstract data type (ADT) is a formal description of the behavior of a data type. This means that an ADT is where we define what kind of operations we would like the data type to have.
We then described the ADT for a list and decided that a list should have the following operations:
Java has a special kind of class that can be used to define an ADT. It is called an Interface. Here is an example of an interface for a list:
public interface TheList<DataType> {
// Add an item to the list
public void add(DataType item);
// Remove an item from the list
public boolean remove(DataType item);
// Retrieve an item from the list by index
public DataType get(int index);
// Return the number of items in the list
public int size();
}
If we were to build a data structure that implements the four methods specified in TheList
interface, we could say that the data structure implements TheList
ADT.
When creating an interface for an ADT, we only need to specify the prototypes for any methods we want any data structures that implement that ADT to have, we don’t specify any method bodies. That is because an ADT only specifies the high-level operations we want, it doesn’t specify how they should be implemented.
We’ll talk more about interfaces in a later set of notes, but for now the most important take-away is that an interface is a convenient way to specify an ADT.
A stack is a data structure that is LIFO (Last-In-First-Out). You can picture it as a stack of items where you put new items on the top and also, remove items from the top.
Here is a simple interface that could be used to describe a stack:
public interface Stack<DataType> {
// Return true if the stack is empty, false otherwise
public boolean isEmpty();
// Put a new item on top of the stack
public void push(DataType value);
// Remove the top item from the top of the stack and return it
public DataType pop();
// Return the top item from the stack, but don't remove it from the stack
public DataType peek();
}
Here are some real-world examples of stack usage:
Stacks of plates at a buffet. (New plates are put on top and plates are also removed from the top.)
“Undo” operations in programs. (You undo the last change made.)
RPN calculators.
A queue is a data structure that is FIFO (First-In-First-Out). You can picture it as a tube. You put items into one end, but you remove them from the other end.
Here is a simple interface that could be used to describe a queue:
public interface Queue<DataType> {
// Return true if the queue is empty
public boolean isEmpty();
// Put a new item onto the end of the queue
public void enqueue(DataType value);
// Remove an item from the head of the queue and return it
public DataType dequeue();
// Return the item from the head of the queue, but don't remove it from the queue
public DataType peek();
}
Here are some real-world examples of queue usage:
The check-out line at the grocery store.
The list of students on the whiteboard during a busy office hours session.
The way food should be stored and used in your refrigerator.
If you implement a stack with an ArrayList
, can all the operations be \(O(1)\)? Why or why not?
If you implement a stack with a LinkedList
, can all the operations be \(O(1)\)? Why or why not?
If you implement a queue with an ArrayList
, can all the operations be \(O(1)\)? Why or why not?
If you implement a queue with a LinkedList
, can all the operations be \(O(1)\)? Why or why not?