Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

com.phoenixst.plexus
Class DefaultGraph  view DefaultGraph download DefaultGraph.java

java.lang.Object
  extended bycom.phoenixst.plexus.DefaultGraph
All Implemented Interfaces:
Graph, ObservableGraph, java.io.Serializable

public class DefaultGraph
extends java.lang.Object
implements ObservableGraph, java.io.Serializable

A default implementation of the ObservableGraph interface.

Design Criteria

There are many ways of representing graphs in software, and this package uses just one of those for the general implementation. Of the two most basic, adjacency list and adjacency matrix, adjacency list is the most efficient for even remotely sparse graphs. Also, the design constraint that nodes and edges are user-provided objects would have made an adjacency matrix representation much more costly in terms of space (a HashMap would be required to map nodes to indices).

In general, it seems preferable to implement a graph as a HashMap from nodes to their adjacency lists. The alternative (using some non-O(1) access Collection) is just too prohibitive in time for the modest gains in space. There are a few different options for distinguishing out-edges from in-edges in a directed graph:

Directed Graph Implementation Options

  1. A directed graph is two maps, one for out-adjacency lists and one for in adjacency lists.
  2. A directed graph is one map, mapping each node to a Pair of adjacency lists.
  3. A directed graph is one map, mapping each node to a single adjacency list which knows how to distinguish out-edges from in-edges.

For a graph with N nodes, we have the following object counts:
Option HashMap Map.Entry Pair AdjList
#1 2 2N 0 2N
#2 1 N N 2N
#3 1 N 0 N

Given that a Pair is smaller than a Map.Entry, and that #2 requires one lookup per node whereas #1 requires two, we can seee that #2 is clearly better than #1. It even more obvious that #3 is better than #2, but only if the adjacency list implementation allows this. The only tradeoff is that the adjacency lists will be twice as large on average, and so will take longer to traverse. This will normally be much less of an issue than the garbage collection overhead caused by having 2N more objects.

Now, how to represent an adjacency list? All an adjacency list needs to do is associate adjacent nodes with the edges that go to them. I examined 5 different ways of doing this, and considered how time and space efficient each would be when used for the 4 different default graph implementations( Directed vs. Undirected, Simple vs. Multi):

Adjacency List Implementation Options

  1. Map adjacent node -> edge. (Simple only)
  2. Map adjacent node -> Collection of edges. (Multi only)
  3. Collection of ( Pair ( adjacent node, Collection of edges ) ). (Multi only)
  4. Collection of ( Pair ( adjacent node, edge ) ).
  5. Collection of ( Triple( tail node, head node, edge ) ).

For this application, an ArrayList is probably the best Collection implementation since it doesn't allocate an extra object for each thing in it (unlike LinkedList and HashMap). For a graph with N nodes, E edges, and A adjacent nodes, we have the following object counts:
Option HashMap Map.Entry List Pair Triple Digraph Option# Notes
#1 N+1/2N+1 N+2E 0 0/N 0 #2 Simple only
#2 N+1/2N+1 N+2A A 0/N 0 #2 Multi only
#3 1 N N+A/2N+A 2A/N+2A 0 #2 Multi only
#4 1 N N/2N 2E/N+2E 0 #2
#5 1 N N/N 0 E #3
Notes:

The comparisons aren't even really close, especially considering that option #5 allows for the same adjacency list representation for both simple and multi-graphs. And, it just so happens to be the same for both directed and undirected graphs; only how the Triples are interpreted changes.

Since:
1.0
Version:
$Revision: 1.35 $

Nested Class Summary
private  class DefaultGraph.AdjacencyList
          An adjacency list implementation.
private  class DefaultGraph.AllEdgesIteratorImpl
          Private implementation class for edgeIterator().
private static class DefaultGraph.CursorReaper
          A thread which reaps cursors from ALL DefaultGraph instances.
private  class DefaultGraph.CursorReference
          Basically, a WeakReference for a Cursor that knows which graph created it.
private  class DefaultGraph.EdgeIteratorImpl
          Support implementation class for use by edgeIterator( tail, head ).
private  class DefaultGraph.NodeIterator
          Private node iterator implementation.
private  class DefaultGraph.TraverserImpl
          Support implementation class used by both directed and undirected standard traverser methods.
 
Nested classes inherited from class com.phoenixst.plexus.Graph
Graph.Edge
 
Field Summary
private  java.util.List backupCursors
          The backup list of currently iterating cursors.
private  java.util.List cursors
          The list of currently iterating cursors.
private  int edgeSize
          The number of edges in this graph.
private  boolean isDirected
          Whether or not this Graph is directed.
private  boolean isSimple
          Whether or not this Graph is simple.
private  GraphListener[] listenerArray
          An array copy of the listener list, if it hasn't changed.
private  java.util.List listeners
          The list of GraphListeners.
private  java.util.Map nodeMap
          Map from nodes to adjacency lists.
private  java.lang.Object syncObject
          The object to sync upon when iterating over the cursors list, as which list that is can change.
 
Constructor Summary
  DefaultGraph()
          Creates a new directed, multi DefaultGraph.
  DefaultGraph(boolean isDirected, boolean isSimple)
          Creates a new DefaultGraph.
  DefaultGraph(boolean isDirected, boolean isSimple, Graph g)
          Creates a new DefaultGraph which is a copy of the specified Graph.
private DefaultGraph(boolean isDirected, boolean isSimple, int nodeSize)
          Creates a new DefaultGraph with space for the specified number of nodes.
  DefaultGraph(Graph g)
          Creates a new DefaultGraph which is a copy of the specified Graph.
 
Method Summary
 Graph.Edge addEdge(java.lang.Object object, java.lang.Object tail, java.lang.Object head)
          Adds the specified edge to the Graph (optional operation).
 void addGraphListener(GraphListener listener)
          Adds the specified GraphListener which will be notified whenever this ObservableGraph's structure changes.
 boolean addNode(java.lang.Object node)
          Adds node to this Graph (optional operation).
private  void alertEdgeAdded(DefaultGraph.AdjacencyList adj)
          Invoked when an edge has been added to alert any listening Cursors.
private  void alertEdgeRemoved(DefaultGraph.AdjacencyList adj, int index)
          Invoked when an edge has been removed to alert any listening Cursors.
private  DefaultGraph.AdjacencyList checkNode(java.lang.Object node)
          Checks to see whether node is in this Graph.
 void clear()
          Removes all nodes and edges from this Graph (optional operation).
 boolean containsEdge(Graph.Edge edge)
          Returns true if this Graph contains the specified Edge.
 boolean containsNode(java.lang.Object node)
          Returns true if this Graph contains the specified node.
private  GraphListener[] createListenerArray()
          Returns the array copy of the listener list, creating it if it has changed.
private  java.lang.String debugToString()
          A debugging toString method, showing the internal data structures.
 int degree(java.lang.Object node)
          Returns the degree of node, defined as the number of edges incident on node, with self-loops counted twice.
protected  void edgeAdded(Graph.Edge edge)
          Invoked after an edge has been added to this Graph.
 java.util.Iterator edgeIterator()
          Returns an Iterator over all the Edges in this Graph.
 java.util.Iterator edgeIterator(java.lang.Object tail, java.lang.Object head)
          Returns an Iterator over all Edges from this Graph with the specified tail and head.
protected  void edgeRemoved(Graph.Edge edge)
          Invoked after an edge has been removed from this Graph.
 int edgeSize()
          Returns the number of edges in this Graph.
private  void fireEdgeAdded(Graph.Edge edge)
          Sends edge added event to registered listeners.
private  void fireEdgeRemoved(Graph.Edge edge)
          Sends edge removed event to registered listeners.
private  void fireNodeAdded(java.lang.Object node)
          Sends node added event to registered listeners.
private  void fireNodeRemoved(java.lang.Object node)
          Sends node removed event to registered listeners.
 Graph.Edge getEdge(java.lang.Object tail, java.lang.Object head)
          Returns an Edge from this Graph with the specified tail and head, or null if it does not exist.
 int inDegree(java.lang.Object node)
          If this is a directed graph, returns the in degree of node, defined as the number of edges entering node.
 Traverser inTraverser(java.lang.Object node)
          Returns a Traverser from node to all adjacent nodes reachable through edges whose head is the given node.
 boolean isDirected()
          Returns whether or not this Graph is directed.
 boolean isEmpty()
          Returns true if this Graph contains no edges, it may contain nodes.
 boolean isSimple()
          Returns whether or not this Graph is simple.
protected  void nodeAdded(java.lang.Object node)
          Invoked after a node has been added to this Graph.
 java.util.Iterator nodeIterator()
          Returns an Iterator over all the nodes in this Graph.
protected  void nodeRemoved(java.lang.Object node)
          Invoked after a node has been removed from this Graph.
 int nodeSize()
          Returns the number of nodes in this Graph.
 int outDegree(java.lang.Object node)
          If this is a directed graph, returns the out degree of node, defined as the number of edges leaving node.
 Traverser outTraverser(java.lang.Object node)
          Returns a Traverser from node to all adjacent nodes reachable through edges whose tail is the given node.
private  void processEdgeAdded(Graph.Edge edge)
           
private  void processEdgeRemoved(Graph.Edge edge)
           
private  void processNodeAdded(java.lang.Object node)
           
private  void processNodeRemoved(java.lang.Object node)
           
private  void readObject(java.io.ObjectInputStream in)
           
private  void reapCursors()
          Removes all dead cursors.
 boolean removeEdge(Graph.Edge edge)
          Removes the specified Edge from this Graph (optional operation).
 void removeGraphListener(GraphListener listener)
          Removes a previously added GraphListener.
 boolean removeNode(java.lang.Object node)
          Removes node from this Graph (optional operation).
 java.lang.String toString()
          Convert this Object to a human-readable String.
 Traverser traverser(java.lang.Object node)
          Returns a Traverser from node to all adjacent nodes.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

nodeMap

private java.util.Map nodeMap
Map from nodes to adjacency lists.


edgeSize

private int edgeSize
The number of edges in this graph.


isDirected

private boolean isDirected
Whether or not this Graph is directed.


isSimple

private boolean isSimple
Whether or not this Graph is simple.


listeners

private transient java.util.List listeners
The list of GraphListeners.


listenerArray

private transient GraphListener[] listenerArray
An array copy of the listener list, if it hasn't changed.


cursors

private transient java.util.List cursors
The list of currently iterating cursors.


backupCursors

private transient java.util.List backupCursors
The backup list of currently iterating cursors.


syncObject

private transient java.lang.Object syncObject
The object to sync upon when iterating over the cursors list, as which list that is can change.

Constructor Detail

DefaultGraph

private DefaultGraph(boolean isDirected,
                     boolean isSimple,
                     int nodeSize)
Creates a new DefaultGraph with space for the specified number of nodes.


DefaultGraph

public DefaultGraph()
Creates a new directed, multi DefaultGraph.


DefaultGraph

public DefaultGraph(boolean isDirected,
                    boolean isSimple)
Creates a new DefaultGraph.


DefaultGraph

public DefaultGraph(Graph g)
Creates a new DefaultGraph which is a copy of the specified Graph.


DefaultGraph

public DefaultGraph(boolean isDirected,
                    boolean isSimple,
                    Graph g)
Creates a new DefaultGraph which is a copy of the specified Graph. If this Graph and the specified Graph do not match in what sort of graph they are (one directed and the other undirected, e.g.), then a best effort is still made to copy the graph.

Method Detail

nodeAdded

protected void nodeAdded(java.lang.Object node)
Invoked after a node has been added to this Graph.


nodeRemoved

protected void nodeRemoved(java.lang.Object node)
Invoked after a node has been removed from this Graph.


edgeAdded

protected void edgeAdded(Graph.Edge edge)
Invoked after an edge has been added to this Graph.


edgeRemoved

protected void edgeRemoved(Graph.Edge edge)
Invoked after an edge has been removed from this Graph.


checkNode

private DefaultGraph.AdjacencyList checkNode(java.lang.Object node)
Checks to see whether node is in this Graph. If so, return the adjacency list for the node. If not, throw an NoSuchNodeException.


reapCursors

private void reapCursors()
Removes all dead cursors. This should only be called by the reaper thread.


alertEdgeAdded

private void alertEdgeAdded(DefaultGraph.AdjacencyList adj)
Invoked when an edge has been added to alert any listening Cursors.


alertEdgeRemoved

private void alertEdgeRemoved(DefaultGraph.AdjacencyList adj,
                              int index)
Invoked when an edge has been removed to alert any listening Cursors.


processNodeAdded

private void processNodeAdded(java.lang.Object node)

processNodeRemoved

private void processNodeRemoved(java.lang.Object node)

processEdgeAdded

private void processEdgeAdded(Graph.Edge edge)

processEdgeRemoved

private void processEdgeRemoved(Graph.Edge edge)

createListenerArray

private GraphListener[] createListenerArray()
Returns the array copy of the listener list, creating it if it has changed.


fireNodeAdded

private void fireNodeAdded(java.lang.Object node)
Sends node added event to registered listeners.


fireNodeRemoved

private void fireNodeRemoved(java.lang.Object node)
Sends node removed event to registered listeners.


fireEdgeAdded

private void fireEdgeAdded(Graph.Edge edge)
Sends edge added event to registered listeners.


fireEdgeRemoved

private void fireEdgeRemoved(Graph.Edge edge)
Sends edge removed event to registered listeners.


debugToString

private java.lang.String debugToString()
A debugging toString method, showing the internal data structures.


isDirected

public boolean isDirected()
Description copied from interface: Graph
Returns whether or not this Graph is directed.

Specified by:
isDirected in interface Graph

isSimple

public boolean isSimple()
Description copied from interface: Graph
Returns whether or not this Graph is simple.

Specified by:
isSimple in interface Graph

isEmpty

public boolean isEmpty()
Description copied from interface: Graph
Returns true if this Graph contains no edges, it may contain nodes.

Specified by:
isEmpty in interface Graph

nodeSize

public int nodeSize()
Description copied from interface: Graph
Returns the number of nodes in this Graph. If this Graph contains more than Integer.MAX_VALUE nodes, returns Integer.MAX_VALUE.

Specified by:
nodeSize in interface Graph

edgeSize

public int edgeSize()
Description copied from interface: Graph
Returns the number of edges in this Graph. If this Graph contains more than Integer.MAX_VALUE edges, returns Integer.MAX_VALUE.

Specified by:
edgeSize in interface Graph

degree

public int degree(java.lang.Object node)
Description copied from interface: Graph
Returns the degree of node, defined as the number of edges incident on node, with self-loops counted twice. If this node has more than Integer.MAX_VALUE incident edges, returns Integer.MAX_VALUE.

Specified by:
degree in interface Graph

outDegree

public int outDegree(java.lang.Object node)
Description copied from interface: Graph
If this is a directed graph, returns the out degree of node, defined as the number of edges leaving node. If this is not a directed graph, throws UnsupportedOperationException. If this node has more than Integer.MAX_VALUE edges leaving it, returns Integer.MAX_VALUE.

Specified by:
outDegree in interface Graph

inDegree

public int inDegree(java.lang.Object node)
Description copied from interface: Graph
If this is a directed graph, returns the in degree of node, defined as the number of edges entering node. If this is not a directed graph, throws UnsupportedOperationException. If this node has more than Integer.MAX_VALUE edges entering it, returns Integer.MAX_VALUE.

Specified by:
inDegree in interface Graph

addNode

public boolean addNode(java.lang.Object node)
Description copied from interface: Graph
Adds node to this Graph (optional operation). Returns true if this Graph changed as a result of the call. Returns false if this Graph already contains node.

If a Graph refuses to add a particular node for any reason other than that it already contains the node, it must throw an exception (rather than returning false). This preserves the invariant that a Graph always contains the specified node after this call returns. Graph classes should clearly specify in their documentation any other restrictions on what nodes may be added.

Specified by:
addNode in interface Graph

removeNode

public boolean removeNode(java.lang.Object node)
Description copied from interface: Graph
Removes node from this Graph (optional operation). This method will also remove all edges incident upon node.

Specified by:
removeNode in interface Graph

containsNode

public boolean containsNode(java.lang.Object node)
Description copied from interface: Graph
Returns true if this Graph contains the specified node.

Specified by:
containsNode in interface Graph

addEdge

public Graph.Edge addEdge(java.lang.Object object,
                          java.lang.Object tail,
                          java.lang.Object head)
Description copied from interface: Graph
Adds the specified edge to the Graph (optional operation). Returns the newly created Edge if this Graph changed as a result of the call. Returns null if this Graph does not allow duplicate edges and already contains the specified edge.

If a Graph refuses to add a particular edge for any reason other than that it already contains the edge, it must throw an exception (rather than returning null). This preserves the invariant that a Graph always contains the specified edge after this call returns. Graph classes should clearly specify in their documentation any other restrictions on what edges may be added.

Specified by:
addEdge in interface Graph

removeEdge

public boolean removeEdge(Graph.Edge edge)
Description copied from interface: Graph
Removes the specified Edge from this Graph (optional operation).

Specified by:
removeEdge in interface Graph

containsEdge

public boolean containsEdge(Graph.Edge edge)
Description copied from interface: Graph
Returns true if this Graph contains the specified Edge.

Specified by:
containsEdge in interface Graph

getEdge

public Graph.Edge getEdge(java.lang.Object tail,
                          java.lang.Object head)
Description copied from interface: Graph
Returns an Edge from this Graph with the specified tail and head, or null if it does not exist.

Specified by:
getEdge in interface Graph

clear

public void clear()
Description copied from interface: Graph
Removes all nodes and edges from this Graph (optional operation).

Specified by:
clear in interface Graph

nodeIterator

public java.util.Iterator nodeIterator()
Returns an Iterator over all the nodes in this Graph. Unlike the other iterators, this one is not tolerant of node changes to the underlying Graph.

null

Specified by:
nodeIterator in interface Graph

edgeIterator

public java.util.Iterator edgeIterator()
Returns an Iterator over all the Edges in this Graph.

The returned Iterator is tolerant of edge changes to the underlying Graph, but is not tolerant of node changes. Note that this does not mean the Iterator is thread-safe. However, if an edge is added or removed while the iteration is is progress, the iteration will not throw a ConcurrentModificationException. In fact, its state will reflect the changes. This means that, among other things, you should always call DefaultGraph.NodeIterator.hasNext() 55 before DefaultGraph.NodeIterator.next() 55 if there is a chance the structure has changed since the last call to hasNext().

null

Specified by:
edgeIterator in interface Graph

edgeIterator

public java.util.Iterator edgeIterator(java.lang.Object tail,
                                       java.lang.Object head)
Returns an Iterator over all Edges from this Graph with the specified tail and head.

The returned Iterator is tolerant of changes to the underlying Graph. Note that this does not mean the Iterator is thread-safe. However, if a node or edge is added or removed while the iteration is is progress, the iteration will not throw a ConcurrentModificationException. In fact, its state will reflect the changes. This means that, among other things, you should always call DefaultGraph.NodeIterator.hasNext() 55 before DefaultGraph.NodeIterator.next() 55 if there is a chance the structure has changed since the last call to hasNext(). The one exception is that if the specified tail node is removed, then all operations except hasNext() will throw a ConcurrentModificationException.

null

Specified by:
edgeIterator in interface Graph

traverser

public Traverser traverser(java.lang.Object node)
Returns a Traverser from node to all adjacent nodes.

The returned Traverser is tolerant of changes to the underlying Graph. Note that this does not mean the Traverser is thread-safe. However, if a node or edge is added or removed while the iteration is is progress, the iteration will not throw a ConcurrentModificationException. In fact, its state will reflect the changes. This means that, among other things, you should always call Iterator.hasNext()>Iterator.hasNext() 55 before Iterator.next()>Iterator.next() 55 if there is a chance the structure has changed since the last call to hasNext(). The one exception is that if the node upon which the returned Traverser is based is removed, then all operations except hasNext() will throw a ConcurrentModificationException.

null

Specified by:
traverser in interface Graph

outTraverser

public Traverser outTraverser(java.lang.Object node)
Returns a Traverser from node to all adjacent nodes reachable through edges whose tail is the given node.

The returned Traverser is tolerant of changes to the underlying Graph. Note that this does not mean the Traverser is thread-safe. However, if a node or edge is added or removed while the iteration is is progress, the iteration will not throw a ConcurrentModificationException. In fact, its state will reflect the changes. This means that, among other things, you should always call Iterator.hasNext()>Iterator.hasNext() 55 before Iterator.next()>Iterator.next() 55 if there is a chance the structure has changed since the last call to hasNext(). The one exception is that if the node upon which the returned Traverser is based is removed, then all operations except hasNext() will throw a ConcurrentModificationException.

null

Specified by:
outTraverser in interface Graph

inTraverser

public Traverser inTraverser(java.lang.Object node)
Returns a Traverser from node to all adjacent nodes reachable through edges whose head is the given node.

The returned Traverser is tolerant of changes to the underlying Graph. Note that this does not mean the Traverser is thread-safe. However, if a node or edge is added or removed while the iteration is is progress, the iteration will not throw a ConcurrentModificationException. In fact, its state will reflect the changes. This means that, among other things, you should always call Iterator.hasNext()>Iterator.hasNext() 55 before Iterator.next()>Iterator.next() 55 if there is a chance the structure has changed since the last call to hasNext(). The one exception is that if the node upon which the returned Traverser is based is removed, then all operations except hasNext() will throw a ConcurrentModificationException.

null

Specified by:
inTraverser in interface Graph

addGraphListener

public void addGraphListener(GraphListener listener)
Description copied from interface: ObservableGraph
Adds the specified GraphListener which will be notified whenever this ObservableGraph's structure changes.

Specified by:
addGraphListener in interface ObservableGraph

removeGraphListener

public void removeGraphListener(GraphListener listener)
Description copied from interface: ObservableGraph
Removes a previously added GraphListener.

Specified by:
removeGraphListener in interface ObservableGraph

readObject

private void readObject(java.io.ObjectInputStream in)
                 throws java.lang.ClassNotFoundException,
                        java.io.IOException

toString

public java.lang.String toString()
Description copied from class: java.lang.Object
Convert this Object to a human-readable String. There are no limits placed on how long this String should be or what it should contain. We suggest you make it as intuitive as possible to be able to place it into System.out.println() 55 and such.

It is typical, but not required, to ensure that this method never completes abruptly with a java.lang.RuntimeException.

This method will be called when performing string concatenation with this object. If the result is null, string concatenation will instead use "null".

The default implementation returns getClass().getName() + "@" + Integer.toHexString(hashCode()).