JGraphT是一个实现了图数据结构和算法的开源Java库,它实现了多种图:有向图、无向图,有权图、无权图,简单图等等,是一个比较完善的库。它的官网为:JGraphTa Java library of graph theory data structures and algorithmshttps://jgrapht.org Github项目地址为: GitHub - jgrapht/jgrapht: Master repository for the JGraphT projectMaster repository for the JGraphT project. Contribute to jgrapht/jgrapht development by creating an account on GitHub.
https://github.com/jgrapht/jgrapht 在本专栏中,我将带领大家分析JGraphT的源码,同时讲解图论的一些知识。
首先,从Github上下载它的源代码,在Idea中打开:
查看左侧的Project栏,我们可以注意到这个项目分成了这么几个部分:jgrapht-core,jgrapht-demo,jgrapht-dist,jgrapht-ext,jgrapht-guava,jgrapht-io,jgrapht-opt和jgrapht-unimi-dsi。其中,jgrapht-core应该是核心部分。
我们展开jgrapht-core,查看其中的内容:
我们看到了Graph接口。JGraphT库就是一个图论库,在它的核心部分有一个Graph,而且是接口,我们可以判断Graph接口是对图方法的抽象,应该是JGraphT库中最核心的部分。
打开Graph接口,Graph接口的代码如下所示:
/*
* (C) Copyright 2003-2023, by Barak Naveh and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht;
import java.util.Collection;
import java.util.Set;
import java.util.function.Supplier;
import org.jgrapht.graph.DefaultGraphIterables;
/**
* The root interface in the graph hierarchy. A mathematical graph-theory graph object
* <code>G(V,E)</code> contains a set <code>V</code> of vertices and a set <code>
* E</code> of edges. Each edge e=(v1,v2) in E connects vertex v1 to vertex v2. for more information
* about graphs and their related definitions see <a href="http://mathworld.wolfram.com/Graph.html">
* http://mathworld.wolfram.com/Graph.html</a>.
*
* <p>
* This library generally follows the terminology found at:
* <a href="http://mathworld.wolfram.com/topics/GraphTheory.html">
* http://mathworld.wolfram.com/topics/GraphTheory.html</a>. Implementation of this interface can
* provide simple-graphs, multigraphs, pseudographs etc. The package <code>org.jgrapht.graph</code>
* provides a gallery of abstract and concrete graph implementations.
* </p>
*
* <p>
* This library works best when vertices represent arbitrary objects and edges represent the
* relationships between them. Vertex and edge instances may be shared by more than one graph.
* </p>
*
* <p>
* Through generics, a graph can be typed to specific classes for vertices <code>V</code> and edges
* <code>E<T></code>. Such a graph can contain vertices of type <code>V</code> and all
* sub-types and Edges of type <code>
* E</code> and all sub-types.
* </p>
*
* <p>
* For guidelines on vertex and edge classes, see
* <a href="https://github.com/jgrapht/jgrapht/wiki/EqualsAndHashCode">this wiki page</a>.
*
* @param <V> the graph vertex type
* @param <E> the graph edge type
*
* @author Barak Naveh
*/
public interface Graph<V, E>
{
/**
* Returns a set of all edges connecting source vertex to target vertex if such vertices exist
* in this graph. If any of the vertices does not exist or is <code>null</code>, returns
* <code>null</code>. If both vertices exist but no edges found, returns an empty set.
*
* <p>
* In undirected graphs, some of the returned edges may have their source and target vertices in
* the opposite order. In simple graphs the returned set is either singleton set or empty set.
* </p>
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return a set of all edges connecting source vertex to target vertex.
*/
Set<E> getAllEdges(V sourceVertex, V targetVertex);
/**
* Returns an edge connecting source vertex to target vertex if such vertices and such edge
* exist in this graph. Otherwise returns <code>
* null</code>. If any of the specified vertices is <code>null</code> returns <code>null</code>
*
* <p>
* In undirected graphs, the returned edge may have its source and target vertices in the
* opposite order.
* </p>
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return an edge connecting source vertex to target vertex.
*/
E getEdge(V sourceVertex, V targetVertex);
/**
* Return the vertex supplier that the graph uses whenever it needs to create new vertices.
*
* <p>
* A graph uses the vertex supplier to create new vertex objects whenever a user calls method
* {@link Graph#addVertex()}. Users can also create the vertex in user code and then use method
* {@link Graph#addVertex(Object)} to add the vertex.
*
* <p>
* In contrast with the {@link Supplier} interface, the vertex supplier has the additional
* requirement that a new and distinct result is returned every time it is invoked. More
* specifically for a new vertex to be added in a graph <code>v</code> must <i>not</i> be equal
* to any other vertex in the graph. More formally, the graph must not contain any vertex
* <code>v2</code> such that <code>v2.equals(v)</code>.
*
* <p>
* Care must also be taken when interchanging calls to methods {@link Graph#addVertex(Object)}
* and {@link Graph#addVertex()}. In such a case the user must make sure never to add vertices
* in the graph using method {@link Graph#addVertex(Object)}, which are going to be returned in
* the future by the supplied vertex supplier. Such a sequence will result into an
* {@link IllegalArgumentException} when calling method {@link Graph#addVertex()}.
*
* @return the vertex supplier or <code>null</code> if the graph has no such supplier
*/
Supplier<V> getVertexSupplier();
/**
* Return the edge supplier that the graph uses whenever it needs to create new edges.
*
* <p>
* A graph uses the edge supplier to create new edge objects whenever a user calls method
* {@link Graph#addEdge(Object, Object)}. Users can also create the edge in user code and then
* use method {@link Graph#addEdge(Object, Object, Object)} to add the edge.
*
* <p>
* In contrast with the {@link Supplier} interface, the edge supplier has the additional
* requirement that a new and distinct result is returned every time it is invoked. More
* specifically for a new edge to be added in a graph <code>e</code> must <i>not</i> be equal to
* any other edge in the graph (even if the graph allows edge-multiplicity). More formally, the
* graph must not contain any edge <code>e2</code> such that <code>e2.equals(e)</code>.
*
* @return the edge supplier <code>null</code> if the graph has no such supplier
*/
Supplier<E> getEdgeSupplier();
/**
* Creates a new edge in this graph, going from the source vertex to the target vertex, and
* returns the created edge. Some graphs do not allow edge-multiplicity. In such cases, if the
* graph already contains an edge from the specified source to the specified target, then this
* method does not change the graph and returns <code>null</code>.
*
* <p>
* The source and target vertices must already be contained in this graph. If they are not found
* in graph {@link IllegalArgumentException} is thrown.
*
* <p>
* This method creates the new edge <code>e</code> using this graph's edge supplier (see
* {@link #getEdgeSupplier()}). For the new edge to be added <code>e</code> must <i>not</i> be
* equal to any other edge the graph (even if the graph allows edge-multiplicity). More
* formally, the graph must not contain any edge <code>e2</code> such that
* <code>e2.equals(e)</code>. If such <code>
* e2</code> is found then the newly created edge <code>e</code> is abandoned, the method leaves
* this graph unchanged and returns <code>null</code>.
*
* <p>
* If the underlying graph implementation's {@link #getEdgeSupplier()} returns
* <code>null</code>, then this method cannot create edges and throws an
* {@link UnsupportedOperationException}.
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return The newly created edge if added to the graph, otherwise <code>
* null</code>.
*
* @throws IllegalArgumentException if source or target vertices are not found in the graph.
* @throws NullPointerException if any of the specified vertices is <code>null</code>.
* @throws UnsupportedOperationException if the graph was not initialized with an edge supplier
*
* @see #getEdgeSupplier()
*/
E addEdge(V sourceVertex, V targetVertex);
/**
* Adds the specified edge to this graph, going from the source vertex to the target vertex.
* More formally, adds the specified edge, <code>
* e</code>, to this graph if this graph contains no edge <code>e2</code> such that
* <code>e2.equals(e)</code>. If this graph already contains such an edge, the call leaves this
* graph unchanged and returns <code>false</code>. Some graphs do not allow edge-multiplicity.
* In such cases, if the graph already contains an edge from the specified source to the
* specified target, then this method does not change the graph and returns <code>
* false</code>. If the edge was added to the graph, returns <code>
* true</code>.
*
* <p>
* The source and target vertices must already be contained in this graph. If they are not found
* in graph IllegalArgumentException is thrown.
* </p>
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
* @param e edge to be added to this graph.
*
* @return <code>true</code> if this graph did not already contain the specified edge.
*
* @throws IllegalArgumentException if source or target vertices are not found in the graph.
* @throws ClassCastException if the specified edge is not assignment compatible with the class
* of edges produced by the edge factory of this graph.
* @throws NullPointerException if any of the specified vertices is <code>
* null</code>.
*
* @see #addEdge(Object, Object)
* @see #getEdgeSupplier()
*/
boolean addEdge(V sourceVertex, V targetVertex, E e);
/**
* Creates a new vertex in this graph and returns it.
*
* <p>
* This method creates the new vertex <code>v</code> using this graph's vertex supplier (see
* {@link #getVertexSupplier()}). For the new vertex to be added <code>v</code> must <i>not</i>
* be equal to any other vertex in the graph. More formally, the graph must not contain any
* vertex <code>v2</code> such that <code>v2.equals(v)</code>. If such <code>
* v2</code> is found then the newly created vertex <code>v</code> is abandoned, the method
* leaves this graph unchanged and throws an {@link IllegalArgumentException}.
*
* <p>
* If the underlying graph implementation's {@link #getVertexSupplier()} returns
* <code>null</code>, then this method cannot create vertices and throws an
* {@link UnsupportedOperationException}.
*
* <p>
* Care must also be taken when interchanging calls to methods {@link Graph#addVertex(Object)}
* and {@link Graph#addVertex()}. In such a case the user must make sure never to add vertices
* in the graph using method {@link Graph#addVertex(Object)}, which are going to be returned in
* the future by the supplied vertex supplier. Such a sequence will result into an
* {@link IllegalArgumentException} when calling method {@link Graph#addVertex()}.
*
* @return The newly created vertex if added to the graph.
*
* @throws IllegalArgumentException if the graph supplier returns a vertex which is already in
* the graph
* @throws UnsupportedOperationException if the graph was not initialized with a vertex supplier
*
* @see #getVertexSupplier()
*/
V addVertex();
/**
* Adds the specified vertex to this graph if not already present. More formally, adds the
* specified vertex, <code>v</code>, to this graph if this graph contains no vertex
* <code>u</code> such that <code>
* u.equals(v)</code>. If this graph already contains such vertex, the call leaves this graph
* unchanged and returns <code>false</code>. In combination with the restriction on
* constructors, this ensures that graphs never contain duplicate vertices.
*
* @param v vertex to be added to this graph.
*
* @return <code>true</code> if this graph did not already contain the specified vertex.
*
* @throws NullPointerException if the specified vertex is <code>
* null</code>.
*/
boolean addVertex(V v);
/**
* Returns <code>true</code> if and only if this graph contains an edge going from the source
* vertex to the target vertex. In undirected graphs the same result is obtained when source and
* target are inverted. If any of the specified vertices does not exist in the graph, or if is
* <code>
* null</code>, returns <code>false</code>.
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return <code>true</code> if this graph contains the specified edge.
*/
boolean containsEdge(V sourceVertex, V targetVertex);
/**
* Returns <code>true</code> if this graph contains the specified edge. More formally, returns
* <code>true</code> if and only if this graph contains an edge <code>e2</code> such that
* <code>e.equals(e2)</code>. If the specified edge is <code>null</code> returns
* <code>false</code>.
*
* @param e edge whose presence in this graph is to be tested.
*
* @return <code>true</code> if this graph contains the specified edge.
*/
boolean containsEdge(E e);
/**
* Returns <code>true</code> if this graph contains the specified vertex. More formally, returns
* <code>true</code> if and only if this graph contains a vertex <code>u</code> such that
* <code>u.equals(v)</code>. If the specified vertex is <code>null</code> returns
* <code>false</code>.
*
* @param v vertex whose presence in this graph is to be tested.
*
* @return <code>true</code> if this graph contains the specified vertex.
*/
boolean containsVertex(V v);
/**
* Returns a set of the edges contained in this graph. The set is backed by the graph, so
* changes to the graph are reflected in the set. If the graph is modified while an iteration
* over the set is in progress, the results of the iteration are undefined.
*
* <p>
* The graph implementation may maintain a particular set ordering (e.g. via
* {@link java.util.LinkedHashSet}) for deterministic iteration, but this is not required. It is
* the responsibility of callers who rely on this behavior to only use graph implementations
* which support it.
* </p>
*
* @return a set of the edges contained in this graph.
*/
Set<E> edgeSet();
/**
* Returns the degree of the specified vertex.
*
* <p>
* A degree of a vertex in an undirected graph is the number of edges touching that vertex.
* Edges with same source and target vertices (self-loops) are counted twice.
*
* <p>
* In directed graphs this method returns the sum of the "in degree" and the "out degree".
*
* @param vertex vertex whose degree is to be calculated.
* @return the degree of the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
* @throws ArithmeticException if the result overflows an int
*/
int degreeOf(V vertex);
/**
* Returns a set of all edges touching the specified vertex. If no edges are touching the
* specified vertex returns an empty set.
*
* @param vertex the vertex for which a set of touching edges is to be returned.
* @return a set of all edges touching the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
*/
Set<E> edgesOf(V vertex);
/**
* Returns the "in degree" of the specified vertex.
*
* <p>
* The "in degree" of a vertex in a directed graph is the number of inward directed edges from
* that vertex. See <a href="http://mathworld.wolfram.com/Indegree.html">
* http://mathworld.wolfram.com/Indegree.html</a>.
*
* <p>
* In the case of undirected graphs this method returns the number of edges touching the vertex.
* Edges with same source and target vertices (self-loops) are counted twice.
*
* @param vertex vertex whose degree is to be calculated.
* @return the degree of the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
* @throws ArithmeticException if the result overflows an int
*/
int inDegreeOf(V vertex);
/**
* Returns a set of all edges incoming into the specified vertex.
*
* <p>
* In the case of undirected graphs this method returns all edges touching the vertex, thus,
* some of the returned edges may have their source and target vertices in the opposite order.
*
* @param vertex the vertex for which the list of incoming edges to be returned.
* @return a set of all edges incoming into the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
*/
Set<E> incomingEdgesOf(V vertex);
/**
* Returns the "out degree" of the specified vertex.
*
* <p>
* The "out degree" of a vertex in a directed graph is the number of outward directed edges from
* that vertex. See <a href="http://mathworld.wolfram.com/Outdegree.html">
* http://mathworld.wolfram.com/Outdegree.html</a>.
*
* <p>
* In the case of undirected graphs this method returns the number of edges touching the vertex.
* Edges with same source and target vertices (self-loops) are counted twice.
*
* @param vertex vertex whose degree is to be calculated.
* @return the degree of the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
* @throws ArithmeticException if the result overflows an int
*/
int outDegreeOf(V vertex);
/**
* Returns a set of all edges outgoing from the specified vertex.
*
* <p>
* In the case of undirected graphs this method returns all edges touching the vertex, thus,
* some of the returned edges may have their source and target vertices in the opposite order.
*
* @param vertex the vertex for which the list of outgoing edges to be returned.
* @return a set of all edges outgoing from the specified vertex.
*
* @throws IllegalArgumentException if vertex is not found in the graph.
* @throws NullPointerException if vertex is <code>null</code>.
*/
Set<E> outgoingEdgesOf(V vertex);
/**
* Removes all the edges in this graph that are also contained in the specified edge collection.
* After this call returns, this graph will contain no edges in common with the specified edges.
* This method will invoke the {@link #removeEdge(Object)} method.
*
* @param edges edges to be removed from this graph.
*
* @return <code>true</code> if this graph changed as a result of the call
*
* @throws NullPointerException if the specified edge collection is <code>
* null</code>.
*
* @see #removeEdge(Object)
* @see #containsEdge(Object)
*/
boolean removeAllEdges(Collection<? extends E> edges);
/**
* Removes all the edges going from the specified source vertex to the specified target vertex,
* and returns a set of all removed edges. Returns <code>null</code> if any of the specified
* vertices does not exist in the graph. If both vertices exist but no edge is found, returns an
* empty set. This method will either invoke the {@link #removeEdge(Object)} method, or the
* {@link #removeEdge(Object, Object)} method.
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return the removed edges, or <code>null</code> if either vertex is not part of graph
*/
Set<E> removeAllEdges(V sourceVertex, V targetVertex);
/**
* Removes all the vertices in this graph that are also contained in the specified vertex
* collection. After this call returns, this graph will contain no vertices in common with the
* specified vertices. This method will invoke the {@link #removeVertex(Object)} method.
*
* @param vertices vertices to be removed from this graph.
*
* @return <code>true</code> if this graph changed as a result of the call
*
* @throws NullPointerException if the specified vertex collection is <code>
* null</code>.
*
* @see #removeVertex(Object)
* @see #containsVertex(Object)
*/
boolean removeAllVertices(Collection<? extends V> vertices);
/**
* Removes an edge going from source vertex to target vertex, if such vertices and such edge
* exist in this graph. Returns the edge if removed or <code>null</code> otherwise.
*
* @param sourceVertex source vertex of the edge.
* @param targetVertex target vertex of the edge.
*
* @return The removed edge, or <code>null</code> if no edge removed.
*/
E removeEdge(V sourceVertex, V targetVertex);
/**
* Removes the specified edge from the graph. Removes the specified edge from this graph if it
* is present. More formally, removes an edge <code>
* e2</code> such that <code>e2.equals(e)</code>, if the graph contains such edge. Returns
* <code>true</code> if the graph contained the specified edge. (The graph will not contain the
* specified edge once the call returns).
*
* <p>
* If the specified edge is <code>null</code> returns <code>
* false</code>.
* </p>
*
* @param e edge to be removed from this graph, if present.
*
* @return <code>true</code> if and only if the graph contained the specified edge.
*/
boolean removeEdge(E e);
/**
* Removes the specified vertex from this graph including all its touching edges if present.
* More formally, if the graph contains a vertex <code>
* u</code> such that <code>u.equals(v)</code>, the call removes all edges that touch
* <code>u</code> and then removes <code>u</code> itself. If no such <code>u</code> is found,
* the call leaves the graph unchanged. Returns <code>true</code> if the graph contained the
* specified vertex. (The graph will not contain the specified vertex once the call returns).
*
* <p>
* If the specified vertex is <code>null</code> returns <code>
* false</code>.
* </p>
*
* @param v vertex to be removed from this graph, if present.
*
* @return <code>true</code> if the graph contained the specified vertex; <code>false</code>
* otherwise.
*/
boolean removeVertex(V v);
/**
* Returns a set of the vertices contained in this graph. The set is backed by the graph, so
* changes to the graph are reflected in the set. If the graph is modified while an iteration
* over the set is in progress, the results of the iteration are undefined.
*
* <p>
* The graph implementation may maintain a particular set ordering (e.g. via
* {@link java.util.LinkedHashSet}) for deterministic iteration, but this is not required. It is
* the responsibility of callers who rely on this behavior to only use graph implementations
* which support it.
* </p>
*
* @return a set view of the vertices contained in this graph.
*/
Set<V> vertexSet();
/**
* Returns the source vertex of an edge. For an undirected graph, source and target are
* distinguishable designations (but without any mathematical meaning).
*
* @param e edge of interest
*
* @return source vertex
*/
V getEdgeSource(E e);
/**
* Returns the target vertex of an edge. For an undirected graph, source and target are
* distinguishable designations (but without any mathematical meaning).
*
* @param e edge of interest
*
* @return target vertex
*/
V getEdgeTarget(E e);
/**
* Get the graph type. The graph type can be used to query for additional metadata such as
* whether the graph supports directed or undirected edges, self-loops, multiple (parallel)
* edges, weights, etc.
*
* @return the graph type
*/
GraphType getType();
/**
* The default weight for an edge.
*/
double DEFAULT_EDGE_WEIGHT = 1.0;
/**
* Returns the weight assigned to a given edge. Unweighted graphs return 1.0 (as defined by
* {@link #DEFAULT_EDGE_WEIGHT}), allowing weighted-graph algorithms to apply to them when
* meaningful.
*
* @param e edge of interest
* @return edge weight
*/
double getEdgeWeight(E e);
/**
* Assigns a weight to an edge.
*
* @param e edge on which to set weight
* @param weight new weight for edge
* @throws UnsupportedOperationException if the graph does not support weights
*/
void setEdgeWeight(E e, double weight);
/**
* Assigns a weight to an edge between <code>sourceVertex</code> and <code>targetVertex</code>.
* If no edge exists between <code>sourceVertex</code> and <code>targetVertex</code> or either
* of these vertices is <code>null</code>, a <code>NullPointerException</code> is thrown.
* <p>
* When there exist multiple edges between <code>sourceVertex</code> and
* <code>targetVertex</code>, consider using {@link #setEdgeWeight(Object, double)} instead.
*
* @param sourceVertex source vertex of the edge
* @param targetVertex target vertex of the edge
* @param weight new weight for edge
* @throws UnsupportedOperationException if the graph does not support weights
*/
default void setEdgeWeight(V sourceVertex, V targetVertex, double weight)
{
this.setEdgeWeight(this.getEdge(sourceVertex, targetVertex), weight);
}
/**
* Access the graph using the {@link GraphIterables} interface. This allows accessing graphs
* without the restrictions imposed by 32-bit arithmetic. Moreover, graph implementations are
* free to implement this interface without explicitly materializing intermediate results.
*
* @return the graph iterables
*/
default GraphIterables<V, E> iterables()
{
return new DefaultGraphIterables<V, E>(this);
}
}
我们发现,import语句下面就写着“The root interface in the graph hierarchy”,这说明它就是这个库的核心。我们的源码分析就从Graph接口开始。
这里要说一个问题。新手在分析别人项目的源代码的时候,往往因为项目太大而无从下手。要解决这个问题,就要知道如何选择分析的入口和顺序。一般可以按照从核心到外围的顺序,或者按代码执行顺序分析。第一种方法要找最核心的类或文件,从最核心的类/文件开始分析;第二种方法要找项目的入口函数,从入口函数开始分析。我们现在要分析的是一个库,就选择从核心到外围的顺序。
我们看到Graph接口的注释很多,直接看代码不方便。所以,我们要构建UML图,可以使用draw.io工具,下载地址:Release 22.1.2 · jgraph/drawio-desktop · GitHubOfficial electron build of draw.io. Contribute to jgraph/drawio-desktop development by creating an account on GitHub.https://github.com/jgraph/drawio-desktop/releases/tag/v22.1.2
Graph接口的UML图如下:
通过UML图,我们就可以看出一个图至少有哪些方法了。