Data Structures

Fixed-sized data structures such as single and double-subscripted arrays do not grow or shrink, where as dynamic data structures can grow and shrink at execution time. There are a number of dynamic data structures that grow and shrink

Linked Lists are collections of data items "lined up in a row", insertions and deletions can be made anywhere in the linked list
Stacks are used in compilers and operating systems, insertions and deletions are made only at one end of the stack - its top.
Queues represent waiting lines, insertion are made at the back (known as the tail) and deletions are made at the front (known as the head) of the queue
Trees facilitate high-speed searching and sorting of data, efficent elimination of duplicate data items, representing file system directories and compiling expressions into machine language

I have touched on Collections before, so you may want to take a peek.

Self-Referential Classes

A self-referential class contains a reference member that refers to a class object of the same class type

example class Node {
   private int data;

   public Node( int data){ ... };                   // constructor
   public void setNext( Node nextNode){ ... };      // method body
   public Node getNext(){ ... };                    // method body
}

Self-referential objects can be linked together to form useful data structures such as lists, queues, stacks and trees.

Dynamic Memory Allocation

Java programs do not release dynamically allocated memory, Java performs automatic garbage collection. The limit for dynamic memory allocation can be as large as the amount of available physical memory and available disk space in a virtual-memory system.

See Garbage collection for more details on how Java gets back the memory that is no longer used.

Linked Lists

A linked list is a linear collection of self-referential class objects, called nodes, connected by reference links -hence, the term linked lists.

**** linked list picture ****

A linked list is accessed by the first node in the list, each subsequent node is accessed via the link-reference member stored in the previous node, the last nodes reference is set to null. Linked lists are dynamic so they can increase or decrease in size. You can keep the linked list on sorted oreder as additional nodes can be placed anywhere in the structure, however the nodes may not be held in a contiguous piece of memory and may well be scattered.

Linked List example
// ListNode Class

class ListNode {
   // package access data so class List can access it directly
   Object data;    
   ListNode next;

   // Constructor: Create a ListNode that refers to Object o.
   ListNode( Object o ) { this( o, null ); }

   // Constructor: Create a ListNode that refers to Object o and
   // to the next ListNode in the List.
   ListNode( Object o, ListNode nextNode )
   {
      data = o;         // this node refers to Object o
      next = nextNode;  // set next to refer to next
   }

   // Return a reference to the Object in this node
   Object getObject() { return data; }

   // Return the next node
   ListNode getNext() { return next; }
}

// Class List definition
public class List {
   private ListNode firstNode;
   private ListNode lastNode;
   private String name;  // String like "list" used in printing

   // Constructor: Construct an empty List with s as the name
   public List( String s )
   {
      name = s;
      firstNode = lastNode = null;
   }

   // Constructor: Construct an empty List with
   // "list" as the name
   public List() { this( "list" ); }                             

   // Insert an Object at the front of the List
   // If List is empty, firstNode and lastNode will refer to
   // the same object. Otherwise, firstNode refers to new node.
   public synchronized void insertAtFront( Object insertItem )
   {
      if ( isEmpty() )
         firstNode = lastNode = new ListNode( insertItem );
      else 
         firstNode = new ListNode( insertItem, firstNode );
   }

   // Insert an Object at the end of the List
   // If List is empty, firstNode and lastNode will refer to
   // the same Object. Otherwise, lastNode's next instance
   // variable refers to new node.
   public synchronized void insertAtBack( Object insertItem )
   {
      if ( isEmpty() )
         firstNode = lastNode = new ListNode( insertItem );
      else 
         lastNode = lastNode.next = new ListNode( insertItem );
   }

   // Remove the first node from the List.
   public synchronized Object removeFromFront()
          throws EmptyListException
   {
      Object removeItem = null;

      if ( isEmpty() )
         throw new EmptyListException( name );

      removeItem = firstNode.data;  // retrieve the data

      // reset the firstNode and lastNode references
      if ( firstNode.equals( lastNode ) )
         firstNode = lastNode = null;
      else
         firstNode = firstNode.next;

      return removeItem;  
   }

   // Remove the last node from the List.
   public synchronized Object removeFromBack()
          throws EmptyListException
   {
      Object removeItem = null;

      if ( isEmpty() )
         throw new EmptyListException( name );

      removeItem = lastNode.data;  // retrieve the data

      // reset the firstNode and lastNode references
      if ( firstNode.equals( lastNode ) )
         firstNode = lastNode = null;
      else {
         ListNode current = firstNode;

         while ( current.next != lastNode )  // not last node
            current = current.next;      // move to next node
   
         lastNode = current;
         current.next = null;
      }

      return removeItem;
   }

   // Return true if the List is empty
   public synchronized boolean isEmpty()
      { return firstNode == null; }

   // Output the List contents
   public synchronized void print()
   {
      if ( isEmpty() ) {
         System.out.println( "Empty " + name );
         return;
      }

      System.out.print( "The " + name + " is: " );

      ListNode current = firstNode;

      while ( current != null ) {
         System.out.print( current.data.toString() + " " );
         current = current.next;
      }

      System.out.println( "\n" );
   }
}

// EmptyListException Class
public class EmptyListException extends RuntimeException {
   public EmptyListException( String name )
   {
      super( "The " + name + " is empty" );
   }
}


// ListTest Class
import List;
import EmptyListException;

public class ListTest {
   public static void main( String args[] )
   {
      List objList = new List();  // create the List container

      // Create objects to store in the List
      Boolean b = Boolean.TRUE;
      Character c = new Character( '$' );
      Integer i = new Integer( 34567 );
      String s = "hello";

      // Use the List insert methods
      objList.insertAtFront( b );
      objList.print();
      objList.insertAtFront( c );
      objList.print();
      objList.insertAtBack( i );
      objList.print();
      objList.insertAtBack( s );
      objList.print();

      // Use the List remove methods
      Object removedObj;

      try {
         removedObj = objList.removeFromFront();
         System.out.println(
            removedObj.toString() + " removed" );
         objList.print();
         removedObj = objList.removeFromFront();
         System.out.println(
            removedObj.toString() + " removed" );
         objList.print();
         removedObj = objList.removeFromBack();
         System.out.println(
            removedObj.toString() + " removed" );
         objList.print();
         removedObj = objList.removeFromBack();
         System.out.println(
            removedObj.toString() + " removed" );
         objList.print();
      }
      catch ( EmptyListException e ) {
         System.err.println( "\n" + e.toString() );
      }
   }
}

Stacks

A stack is a constrained version of a link list, new nodes can be added to a stack and removed from the stack only at the top, a stack is sometimes referred to as a last-in first-out (LIFO) data structure.The two main methods used on a stack are push (add) and pop (remove).

*** stack picture ***

Simple Stack example
// StackInheritance Class
public class StackInheritance extends List { public StackInheritance() { super( "stack" ); } public void push( Object o ) { insertAtFront( o ); } public Object pop() throws EmptyListException { return removeFromFront(); } public boolean isEmpty() { return super.isEmpty(); } public void print() { super.print(); } } // StackInheritanceTest
import StackInheritance; import EmptyListException; public class StackInheritanceTest { public static void main( String args[] ) { StackInheritance objStack = new StackInheritance(); // Create objects to store in the stack Boolean b = Boolean.TRUE; Character c = new Character( '$' ); Integer i = new Integer( 34567 ); String s = "hello"; // Use the push method objStack.push( b ); objStack.print(); objStack.push( c ); objStack.print(); objStack.push( i ); objStack.print(); objStack.push( s ); objStack.print(); // Use the pop method Object removedObj = null; try { while ( true ) { removedObj = objStack.pop(); System.out.println( removedObj.toString() + " popped" ); objStack.print(); } } catch ( EmptyListException e ) { System.err.println( "\n" + e.toString() ); } } }
Simple stack using composition
// StackComposition Class
public class StackComposition { private List s; public StackComposition() { s = new List( "stack" ); } public void push( Object o ) { s.insertAtFront( o ); } public Object pop() throws EmptyListException { return s.removeFromFront(); } public boolean isEmpty() { return s.isEmpty(); } public void print() { s.print(); } } //StackCompositionTest Class import StackComposition; import EmptyListException; public class StackCompositionTest { public static void main( String args[] ) { StackComposition objStack = new StackComposition(); // Create objects to store in the stack Boolean b = Boolean.TRUE; Character c = new Character( '$' ); Integer i = new Integer( 34567 ); String s = "hello"; // Use the push method objStack.push( b ); objStack.print(); objStack.push( c ); objStack.print(); objStack.push( i ); objStack.print(); objStack.push( s ); objStack.print(); // Use the pop method Object removedObj = null; try { while ( true ) { removedObj = objStack.pop(); System.out.println( removedObj.toString() + " popped" ); objStack.print(); } } catch ( EmptyListException e ) { System.err.println( "\n" + e.toString() ); } } }

Queues

Another common data structure is the queue, this is like a line at a ticket stand, the first person in the line is serviced first and other customers join the line at the end. With queues nodes are removed only from the head of the queue and inserted only at the tail of the queue, this is referred to as first-in, first-out (FIFO) data structure. The insert and remove operations are sometimes referred to as enqueue and dequeue.

*** queue picture ***

Queue example
// QueueInheritance Class
public class QueueInheritance extends List {
   public QueueInheritance() { super( "queue" ); }
   public void enqueue( Object o )
      { insertAtBack( o ); }
   public Object dequeue()
      throws EmptyListException { return removeFromFront(); }
   public boolean isEmpty() { return super.isEmpty(); }
   public void print() { super.print(); }
}


// QueueInheritanceTest Class
import QueueInheritance; import EmptyListException; public class QueueInheritanceTest { public static void main( String args[] ) { QueueInheritance objQueue = new QueueInheritance(); // Create objects to store in the queue Boolean b = Boolean.TRUE; Character c = new Character( '$' ); Integer i = new Integer( 34567 ); String s = "hello"; // Use the enqueue method objQueue.enqueue( b ); objQueue.print(); objQueue.enqueue( c ); objQueue.print(); objQueue.enqueue( i ); objQueue.print(); objQueue.enqueue( s ); objQueue.print(); // Use the dequeue method Object removedObj = null; try { while ( true ) { removedObj = objQueue.dequeue(); System.out.println( removedObj.toString() + " dequeued" ); objQueue.print(); } } catch ( EmptyListException e ) { System.err.println( "\n" + e.toString() ); } } }

Trees

Linked lists, stacks and queues are all linear data structures (sequences). A tree is a non-linear, two dimensional data structure with special properties. Tree nodes contain two or more links, a binary tree contains two links whose nodes all contain two links (none, one or both of which may be null). The root node is the first node in a tree, each link in the root node refers to a child. The left child is the first node in the left subtree and the right child is the first node in the right subtree. The children of a node are called siblings. A node with no children is acalled a leaf node.

*** tree picture ***

Tree example
// TreeNode Class

class TreeNode {
   // package access members
   TreeNode left;   // left node
   int data;        // data item
   TreeNode right;  // right node

   // Constructor: initialize data to d and make this a leaf node
   public TreeNode( int d )
   { 
      data = d;              
      left = right = null;  // this node has no children
   }

   // Insert a TreeNode into a Tree that contains nodes.
   // Ignore duplicate values.
   public synchronized void insert( int d )
   {
      if ( d < data ) {
         if ( left == null )
            left = new TreeNode( d );
         else
            left.insert( d );
      }
      else if ( d > data ) {
         if ( right == null )
            right = new TreeNode( d );
         else
            right.insert( d );
      }
   }
}

// Class Tree definition
public class Tree {
   private TreeNode root;

   // Construct an empty Tree of integers
   public Tree() { root = null; }

   // Insert a new node in the binary search tree.
   // If the root node is null, create the root node here.
   // Otherwise, call the insert method of class TreeNode.
   public synchronized void insertNode( int d )
   {
      if ( root == null )
         root = new TreeNode( d );
      else
         root.insert( d );
   }

   // Preorder Traversal
   public synchronized void preorderTraversal()
      { preorderHelper( root ); }

   // Recursive method to perform preorder traversal
   private void preorderHelper( TreeNode node )
   {
      if ( node == null )
         return;

      System.out.print( node.data + " " );
      preorderHelper( node.left );
      preorderHelper( node.right );
   }

   // Inorder Traversal
   public synchronized void inorderTraversal()
      { inorderHelper( root ); }

   // Recursive method to perform inorder traversal
   private void inorderHelper( TreeNode node )
   {
      if ( node == null )
         return;

      inorderHelper( node.left );
      System.out.print( node.data + " " );
      inorderHelper( node.right );
   }

   // Postorder Traversal
   public synchronized void postorderTraversal()
      { postorderHelper( root ); }

   // Recursive method to perform postorder traversal
   private void postorderHelper( TreeNode node )
   {
      if ( node == null )
         return;

      postorderHelper( node.left );
      postorderHelper( node.right );
      System.out.print( node.data + " " );
   }
}


// TreeTest Class
import Tree;

// Class TreeTest definition
public class TreeTest {
   public static void main( String args[] )
   {
      Tree tree = new Tree();
      int intVal;

      System.out.println( "Inserting the following values: " );

      for ( int i = 1; i <= 10; i++ ) {
         intVal = ( int ) ( Math.random() * 100 );
         System.out.print( intVal + " " );
         tree.insertNode( intVal );
      }

      System.out.println ( "\n\nPreorder traversal" );
      tree.preorderTraversal();

      System.out.println ( "\n\nInorder traversal" );
      tree.inorderTraversal();

      System.out.println ( "\n\nPostorder traversal" );
      tree.postorderTraversal();
      System.out.println();
   }
}