Interfaces for the JUNG graph types, and some representative implementations.

A graph consists of a set of vertices set and a set of edges which connect the vertices. The base interface is `Hypergraph`, which defines the most general type of graph; other interfaces (`Graph`, `DirectedGraph`, etc.) define more restrictive graph types.

Vertex and edge types are specified at compile type using Java 1.5 generics.

Types of graphs which are supported include (but are not limited to)

- edges (these have have exactly two endpoints, which may or may not be distinct)
- self-loops (edges which connect exactly one vertex)
- directed and undirected edges
- vertices and edges with attributes (for example, weighted edges)
- vertices and edges with different constraints or properties (examples: trees, bipartite graphs, or multimodal)
- parallel edges (multiple edges which connect a single set of vertices)
- internal representations as matrices or as adjacency lists or adjacency maps
- internal representations that order their elements according to insertion time, natural ordering, or a specified `Comparator`

Extensions or implementations of this interface may enforce or disallow any or all of these variations.

Notes:

- The collections returned by graph instances should be treated in general as if read-only. While they are not contractually guaranteed (or required) to be immutable, these interfaces do not define the outcome if they are mutated. Mutations should be done via `{add,remove}{Edge,Vertex}`, or in the constructor.
- "Wrapper" graphs are available through `GraphDecorator`; these are useful if you want to create a graph implementation that uses another implementation to do the work, and adds some extra behavior. (One example: `ObservableGraph`, which notifies registered listeners when graph mutations occur.)