When we created a stack in the last chapter, we used an ordinary Java array to hold the stack's data. The stack's push() and pop() operations were actually carried out by array operations such as
arr[++top] = data;
and
data = arr[top--];
which insert data into, and take it out of, an array.
We can also use a linked list to hold a stack's data. In this case the push() and pop()
operations would be carried out by operations like
and
data = theList.deleteFirst()
The user of the stack class calls push() and pop() to insert and delete items, without knowing, or needing to know, whether the stack is implemented as an array or as a linked list. Listing 5.4 shows how a stack class called LinkStack can be implemented using the LinkList class instead of an array. (Object purists would argue that the name
LinkStack should be simply Stack, because users of this class shouldn't need to know that it's implemented as a list.)
Listing 5.4 The linkStack() Program // linkStack.java
// demonstrates a stack implemented as a list // to run this program: C>java LinkStackApp import java.io.*; // for I/O
////////////////////////////////////////////////////////////////
class Link
{
public double dData; // data item
public Link next; // next link in list
// --- -
public Link(double dd) // constructor { dData = dd; }
// --- -
public void displayLink() // display ourself { System.out.print(dData + " "); }
} // end class Link
////////////////////////////////////////////////////////////////
class LinkList
{
private Link first; // ref to first item on list
// --- -
public LinkList() // constructor
{ first = null; } // no items on list yet // --- -
public boolean isEmpty() // true if list is empty { return (first==null); }
// --- -
public void insertFirst(double dd) // insert at start of list
{ // make new link Link newLink = new Link(dd);
newLink.next = first; // newLink --> old first first = newLink; // first --> newLink }
// --- -
public double deleteFirst() // delete first item
{ // (assumes list not empty)
Link temp = first; // save reference to link first = first.next; // delete it: first-->old next
return temp.dData; // return deleted link }
// --- -
public void displayList() {
Link current = first; // start at beginning of list
while(current != null) // until end of list,
{
current.displayLink(); // print data
current = current.next; // move to next link }
System.out.println(""); }
// --- -
} // end class LinkList
////////////////////////////////////////////////////////////////
class LinkStack
{
private LinkList theList;
//--- -
public LinkStack() // constructor {
theList = new LinkList(); }
//--- -
public void push(double j) // put item on top of stack
{
theList.insertFirst(j); }
//--- -
public double pop() // take item from top of stack { return theList.deleteFirst(); } //--- -
public boolean isEmpty() // true if stack is empty {
return ( theList.isEmpty() ); }
//--- -
public void displayStack() { System.out.print("Stack (top-->bottom): "); theList.displayList(); } //--- -
} // end class LinkStack
////////////////////////////////////////////////////////////////
class LinkStackApp
{
public static void main(String[] args) throws IOException {
LinkStack theStack = new LinkStack(); // make stack theStack.push(20); // push items theStack.push(40);
theStack.displayStack(); // display stack theStack.push(60); // push items theStack.push(80);
theStack.displayStack(); // display stack theStack.pop(); // pop items theStack.pop();
theStack.displayStack(); // display stack } // end main()
} // end class LinkStackApp
The main() routine creates a stack object, pushes two items on it, displays the stack, pushes two more items, and displays it again. Finally it pops two items and displays the stack again. Here's the output:
Stack (top-->bottom): 40 20
Stack (top-->bottom): 80 60 40 20 Stack (top-->bottom): 40 20
Notice the overall organization of this program. The main() routine in the
LinkStackApp class relates only to the LinkStack class. The LinkStack class relates only to the LinkList class. There's no communication between main() and the
LinkList class.
More specifically, when a statement in main() calls the push() operation in the
LinkStack class, this method in turn calls insertFirst() in the LinkList class to
sactually insert data. Similarly, pop() calls deleteFirst() to delete an item, and
displayStack() calls displayList() to display the stack. To the class user, writing code in main(), there is no difference between using the list-based LinkStack class and using the array-based stack class from the Stack.java program in Chapter 4.
A Queue Implemented by a Linked List
Here's a similar example of an ADT implemented with a linked list. Listing 5.5 shows a queue implemented as a double-ended linked list.
Listing 5.5 The linkQueue() Program // linkQueue.java
// demonstrates queue implemented as double-ended list // to run this program: C>java LinkQueueApp
import java.io.*; // for I/O
////////////////////////////////////////////////////////////////
class Link
{
public double dData; // data item
public Link next; // next link in list // --- -
public Link(double d) // constructor { dData = d; }
// --- -
public void displayLink() // display this link { System.out.print(dData + " "); }
// --- -
} // end class Link
////////////////////////////////////////////////////////////////
class FirstLastList
{
private Link first; // ref to first item private Link last; // ref to last item
// --- -
public FirstLastList() // constructor {
first = null; // no items on list yet last = null;
}
// --- -
public boolean isEmpty() // true if no links { return first==null; }
// --- -
public void insertLast(double dd) // insert at end of list
{
Link newLink = new Link(dd); // make new link if( isEmpty() ) // if empty list, first = newLink; // first --> newLink else
last.next = newLink; // old last --> newLink last = newLink; // newLink <-- last }
// --- -
public double deleteFirst() // delete first link { // (assumes non-empty list)
double temp = first.dData;
if(first.next == null) // if only one item last = null; // null <-- last first = first.next; // first --> old next return temp;
}
// --- -
public void displayList() {
Link current = first; // start at beginning while(current != null) // until end of list, {
current.displayLink(); // print data
current = current.next; // move to next link }
System.out.println(""); }
// --- -
} // end class FirstLastList
class LinkQueue {
private FirstLastList theList;
//--- -
public LinkQueue() // constructor {
theList = new FirstLastList(); // make a 2-ended list }
//--- -
public boolean isEmpty() // true if queue is empty
{
return theList.isEmpty(); }
//--- -
public void insert(double j) // insert, rear of queue
{
theList.insertLast(j); }
//--- -
public double remove() // remove, front of queue
{
return theList.deleteFirst(); }
//--- -
public void displayQueue() { System.out.print("Queue (front-->rear): "); theList.displayList(); } //--- -
} // end class LinkQueue
////////////////////////////////////////////////////////////////
class LinkQueueApp
{
public static void main(String[] args) throws IOException {
LinkQueue theQueue = new LinkQueue();
theQueue.insert(20); // insert items theQueue.insert(40);
theQueue.displayQueue(); // display queue theQueue.insert(60); // insert items theQueue.insert(80);
theQueue.displayQueue(); // display queue theQueue.remove(); // remove items theQueue.remove();
theQueue.displayQueue(); // display queue } // end main()
} // end class LinkQueueApp
The program creates a queue, inserts two items, inserts two more items, and removes two items; following each of these operations the queue is displayed. Here's the output:
Queue (front-->rear): 20 40
Queue (front-->rear): 20 40 60 80 Queue (front-->rear): 60 80
Here the methods insert() and remove() in the LinkQueue class are implemented by the insertLast() and deleteFirst() methods of the FirstLastList class. We've substituted a linked list for the array used to implement the queue in the Queue
program of Chapter 4.
The LinkStack and LinkQueue programs emphasize that stacks and queues are conceptual entities, separate from their implementations. A stack can be implemented equally well by an array or by a linked list. What's important about a stack is the push()
and pop() operations and how they're used; it's not the underlying mechanism used to implement these operations.
When would you use a linked list as opposed to an array as the implementation of a stack or queue? One consideration is how accurately you can predict the amount of data the stack or queue will need to hold. If this isn't clear, the linked list gives you more flexibility than an array. Both are fast, so that's probably not a major consideration.