The New Graph Class

package graph;

import java.util.Deque;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * This class represents directed and undirected graphs of vertices and
 * provides graph searching operations for them.
 * Graphs are internally represented using adjacency lists.
 * @author tcolburn
 */
public class Graph {

    /**
     * Constructs a new graph given lists of vertices and edges.
     * @param vertices A list of graph vertices
     * @param edges A list of graph edges
     * @param directed Whether the graph is directed or not
     */
    public Graph(List<Vertex> vertices, List<Edge> edges, boolean directed) {

        this.vertices = vertices;
        this.directed = directed;
        adj_hash = new Hashtable<Vertex, List<Vertex>>();
        Iterator<Vertex> vertexIterator = vertices.iterator();
        while ( vertexIterator.hasNext() ) {
            Vertex v = (Vertex) vertexIterator.next();
            adj_hash.put(v, new LinkedList<Vertex>());
        }
        Iterator<Edge> iter = edges.iterator();
        while (iter.hasNext()) {
            Edge e = iter.next();
            addEdge(e.getV1(), e.getV2());
        }
        searchStack = new Stack<Vertex>();
    }

    /**
     * Adds an edge (v1, v2) to this graph.
     * If the graph is undirected, (v2, v1) is also added.
     * @param v1 The first vertex in the edge
     * @param v2 The second vertex in the edge
     */
    private void addEdge(Vertex v1, Vertex v2) {
        adj_hash.get(v1).add(v2);
        if (!directed) {
            adj_hash.get(v2).add(v1);
        }
    }

    /**
     * Tests whether a pair of vertices forms an edge in this graph.
     * @param vertex1 The first vertex
     * @param vertex2 The second vertex
     * @return Whether there is an edge from the first vertex to the second
     */
    public boolean isEdge(Vertex vertex1, Vertex vertex2) {
        return adj_hash.get(vertex1).contains(vertex2);
    }

    /**
     * Performs a breadth-first search of this graph from a given starting vertex.
     * For each vertex that is reachable from the start, this operation computes
     * its distance from the start and its predecessor on the search path.
     * @param start The start vertex
     */
    public void breadthFirstSearch(Vertex start) {

        DequeAdder tailAdder = new DequeAdder() {
            public void add(Vertex vertex, Deque<Vertex> deque) {
                deque.addLast(vertex);
            }
        };

        search(start, tailAdder);
    }

    /**
     * Performs a depth-first search of this graph from a given starting vertex.
     * For each vertex that is reachable from the start, this operation computes
     * its distance from the start and its predecessor on the search path.
     * @param start The start vertex
     */
    public void depthFirstSearch(Vertex start) {

        DequeAdder headAdder = new DequeAdder() {
            public void add(Vertex vertex, Deque<Vertex> deque) {
                deque.addFirst(vertex);
            }
        };

        search(start, headAdder);
    }

    /**
     * Produces a string representation of the search path from a start
     * vertex to an end vertex in this graph.
     * This method is intended to be called after subjecting the graph to
     * a search.
     * The string representation for an individual vertex is obtained
     * using its <b>toString</b> method.
     * @param start The start vertex in the search path
     * @param end The end vertex in the search path
     * @return A string representation of the search path
     */
    public String pathString(Vertex start, Vertex end) {
        if ( start == end ) {
            return vertexString(start);
        }
        else if ( end.getPredecessor() == null ) {
            return "";
        }
        else {
            return pathString(start, end.getPredecessor()) + vertexString(end);
        }
    }

    /**
     * A string representation of a vertex with end-of-lines before and after.
     * @param v A vertex
     * @return The vertex's string representation
     */
    private String vertexString(Vertex v) {
        return "\n" + v.toString() + "\n";
    }

    /**
     * The core search operation for this graph.
     * It uses a double-ended queue so that either breadth-first or
     * depth-first search can be performed depending on to which end of
     * the queue newly discovered vertices are added.
     * @param start The start vertex for the search
     * @param adder A purely functional object that adds to either the 
     * head or tail of a double-ended queue
     */
    private void search(Vertex start, DequeAdder adder) {
        searchInit();
        start.setOpen(false);
        start.setDistance(0);
        Deque<Vertex> deque = new LinkedList<Vertex>();
        deque.add(start);
        while ( !deque.isEmpty() ) {
            Vertex v = deque.remove();
            List<Vertex> adjList = adj_hash.get(v);
            if ( adjList != null ) {
                Iterator iter = adjList.iterator();
                while ( iter.hasNext() ) {
                    Vertex successor = (Vertex) iter.next();
                    if ( successor.isOpen() ) {
                        successor.setOpen(false);
                        successor.setDistance(v.getDistance()+1);
                        successor.setPredecessor(v);
                        adder.add(successor, deque);
                    }
                }
            }
        }
    }

    /**
     * An initializing operation for the core search operation.
     * All vertices are set to open with null predecessors and
     * a distance of inifinity from the start.
     */
    private void searchInit() {
        Iterator<Vertex> iterator = vertices.iterator();
        while ( iterator.hasNext() ) {
            Vertex vertex = (Vertex) iterator.next();
            vertex.setOpen(true);
            vertex.setDistance(INFINITY);
            vertex.setPredecessor(null);
        }
    }

    /**
     * Accessor for a list of vertices making up this graph.
     * @return A list of vertices
     */
    public List<Vertex> getVertices() {
        return vertices;
    }

    /**
     * Creates a string representation of this graph's adjacency list for testing.
     * @return The string representation of the adjacency list
     */
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        Iterator vertexIterator = vertices.iterator();
        while ( vertexIterator.hasNext() ) {
            Vertex vertex = (Vertex) vertexIterator.next();
            buffer.append("\n" + vertex.getName());
            List<Vertex> adjList = adj_hash.get(vertex);
            if ( adjList == null ) continue;
            Iterator listIterator = adjList.iterator();
            while ( listIterator.hasNext() ) {
                Vertex adjVertex = (Vertex) listIterator.next();
                buffer.append(" -> " + adjVertex.getName());
            }
        }
        return buffer.toString();
    }

    /**
     * Whether this graph is directed or not.
     */
    private boolean directed;

    /**
     * The vertex's adjacency lists are stored in a hash table whose
     * keys are vertices and whose values are lists of vertices.
     */
    private Hashtable<Vertex, List<Vertex>> adj_hash;

    /**
     * A list of vertices in this graph.  This list is necessary because a
     * vertex may not get into the hash table if there is no edge from it.
     */
    private List<Vertex> vertices;

    /**
     * A large number used to represent infinity for the search operations.
     * It is public just so test classes can use it.
     */
    public static final int INFINITY = 999999999;

}