modular programming
linked-list representation, maintain pointer to first node in a linked list; insert/remove from front.
array implementation
Defect. Stack overflows when N exceeds capacity. (stay tuned)
Overflow and underflow.
Null items. We allow null items to be inserted.
Loitering. Holding a reference to an object when it is no longer needed.
public String pop() {
return s[--N];
}
public String pop() {
String item = s[--N];
s[N] = null;
return item;
}
this version avoids “loitering”: garbage collector can reclaim memory only if no outstanding references
If array is full, create a new array of twice the size, and copy items.
Efficient solution.
Amortized analysis. Average running time per operation over a worst-case sequence of operations.
Linked-list implementation.
Resizing-array implementation.
That’s a trade off that the client can make if I want that guaranteed, if I want to be sure that every operation is going to be fast use a linked-list and if I don’t need that guarantee, if I just care about the total amount of time I’ll probably use the resizing array because the total will be much less because individual operations are fast.
So, even with these simple data structures, we have really important trade offs that actually make a difference in lots of practical situations.
linked-list representation
resizing array implementation
Attempt 1. Implement a separate stack class for each type.
Attempt 2. Implement a stack with items of type Object.
Attempt 3. Java generics.
Discover type mismatch errors at compile-time instead of run-time.
Stack<Apple> s = new Stack<Apple>();
public class Stack<Item> {
public FixedCapacityStack(int capacity) {
// s = new Item[capacity]; // not allowed in Java
s = (Item[]) new Object[capacity];
}
}
Wrapper type.
Autoboxing. Automatic cast between a primitive type and its wrapper.
Design challenge. Support iteration over stack items by client, without revealing the internal representation of the stack.
Java solution. Make stack implement the Iterable interface.
public interface Iterable<Item> {
Iterator<Item> iterator();
}
public interface Iterator<Item> {
boolean hasNext();
Item next();
void remove();
}
for (String s : stack) {
StdOut.println(s);
}
Iterator<String> i = stack.iterator();
while (i.hasNext()) {
String s = i.next();
StdOut.println(s);
}
import java.util.Iterator;
// linked-list implementation
public class Stack<Item> implements Iterable<Item> {
public Iterator<Item> iterator() {
return new ListIterator();
}
private class ListIterator implements Iterator<Item>
{
private Node current = first;
public boolean hasNext() {
return current != null;
}
public Item next() {
Item item = current.item;
current = current.next;
return item;
}
}
}
// array implementation
public class Stack<Item> implements Iterable<Item> {
public Iterator<Item> iterator() {
return new ReverseArrayIterator();
}
private class ReverseArrayIterator implements Iterator<Item> {
private int i = N;
public boolean hasNext() {
return i > 0;
}
public void remove() {
/* not supported */
}
public Item next() {
return s[--i];
}
}
}
Adding items to a collection and iterating (when order doesn’t matter).
Implementation. Stack (without pop) or queue (without dequeue).
Java collections library
List interface. java.util.List is API for sequence of items.
Implementations.
java.util.ArrayList uses resizing array;
java.util.LinkedList uses linked list.
java.util.Stack
java.util.Queue. An interface, not an implementation of a queue.
Best practices. Use our implementations of Stack, Queue, and Bag.
Dijkstra’s two-stack algorithm
( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )