1. 程式人生 > >Guava學習之GraphsExplained

Guava學習之GraphsExplained

Graphs, Explained

Guava的 common.graph 是一個用於建模圖形結構資料的庫,即實體和它們之間的關係。例子包括網頁和超連結;科學家和他們寫的論文;機場和它們之間的路線;以及人們和他們的家庭關係(家譜)。它的目的是提供一種通用的和可擴充套件的語言來處理這些資料。

Definitions

圖由一組節點(也稱為頂點)和一組邊(也稱為連結或弧)組成;每個邊將節點彼此連線。入射到邊緣的節點稱為其端點。(當我們在下面介紹一個名為Graph的介面時,我們將使用“graph”(小寫“g”)作為對這種型別資料結構的通用術語。當我們想在這個庫中引用一個特定的型別時,我們把它大寫。)

如果有一個定義的開始(源)和結束(它的目標,也稱為它的目的地),則指向一個邊緣。否則,它是無向的。有向邊適合於建模非對稱關係(“從屬”、“連結”、“授權”),而無向邊則適合於對稱關係的建模(“合著一篇論文”、“之間的距離”、“兄弟姐妹”)。

如果每個邊都是有向的,或如果每個邊都是無向的,則是一個圖。(common.graph不支援既有定向也有無向邊的圖)。

例子:

graph.addEdge(nodeU, nodeV, edgeUV);
  • nodeUnodeV  是相互鄰接的
  • edgeUV是從 nodeUnodeV的邊 (反之亦然)

如果 graph 是有向圖:

  • nodeU
    nodeV 的前繼節點
  • nodeVnodeU 的後繼節點
  • edgeUVnodeU 的輸出邊(出邊)
  • edgeUVnodeV 的輸入邊(入邊)
  • nodeUedgeUV 的源節點
  • nodeVedgeUV 的目標節點

如果 graph 是無向圖:

  • nodeU 既是 nodeV 的前繼節點也是後繼節點
  • nodeV 既是 nodeU 的前繼節點也是後繼節點
  • edgeUV 既是 nodeU 的輸入邊也是輸出邊
  • edgeUV 既是  nodeV 的輸入邊也是輸出邊

所有的這些關係都是關於 graph 的。

自迴圈是用邊連線自身,等價地,它是端點是同一節點的邊。如果自環是有向的,則它既是節點的出射邊也是入射邊,並且其既是源節點也是目標節點。

如果兩個邊以相同的順序(如果有的話)連線相同的節點,則它們平行;如果它們以相反的順序連線相同的節點,則反平行。(無向邊不能反平行)

例如:

directedGraph.addEdge(nodeU, nodeV, edgeUV_a); 
directedGraph.addEdge(nodeU, nodeV, edgeUV_b); 
directedGraph.addEdge(nodeV, nodeU, edgeVU); 

undirectedGraph.addEdge(nodeU, nodeV, edgeUV_a);
undirectedGraph.addEdge(nodeU, nodeV, edgeUV_b); 
undirectedGraph.addEdge(nodeV, nodeU, edgeVU);

directedGraph, edgeUV_aedgeUV_b 是互相平行的, 每一個都與 edgeVU 反平行。

undirectedGraphedgeUV_a, edgeUV_b, 與 edgeVU 是兩兩平行的。

Capabilities

common.graph 專注於提供介面與類支援圖的操作。不提供諸如 I/O 或視覺化支援的功能, 並且它對公用事業的選擇非常有限。

請參閱更多關於這個主題的FAQ

總之, common.graph 支援下列型別的圖表:

  • 有向圖
  • 無向圖
  • 有權圖
  • 自環或無自環圖
  • 平行邊或不平行邊圖 (具有平行邊的圖有時稱為多重圖)
  • 節點/邊是插入有序的或無序的圖

common.graph 支援的特定圖在Javadoc中被指定。每個圖形型別的內建實現支援的圖形型別在Javadoc中為其關聯Builder型別指定。 這個庫中的型別的特定實現(尤其是第三方實現)不需要支援所有這些變體,還可以支援其他變體。

該庫對於底層資料結構的選擇是不可知的:根據實現者想要優化的用例,關係可以儲存為矩陣、鄰接列表、鄰接對映等。

common.graph 暫時不包含對以下變體圖的顯式支援,儘管可以使用現有型別對其進行建模:

  • 樹,森林
  • 具有相同種類(節點或邊)具有不同型別的元素的圖(例如:二分圖/k分圖、多峰圖)
  • 超圖

common.graph 不允許圖中既有有向邊也有無向邊。

Graphs 類提供一些基礎工具 (例如, 圖的複製與比較).

Graph Types

有三個頂級圖形介面,它們通過表示邊緣來區分:Graph、ValueGraph和Network。這些是兄弟型別,也就是說,沒有一個是任何其他型別的子型別。

這些頂級介面繼承自 SuccessorsFunctionPredecessorsFunction. 這些介面旨在用作圖形演算法(例如寬度優先遍歷)的引數型別,這些演算法只需要訪問圖中節點的後繼/前繼。這對於圖的所有者已經具有適合他們的表示並且不想為了執行一個圖演算法而將它們的表示序列化為公共的圖型別的情況特別有用。

Graph

Graph 是最簡單也是最基礎的圖的型別. 它定義了用於處理節點到節點關係的低階操作符, 例如 successors(node), adjacentNodes(node), 與 inDegree(node). 它的節點最好是唯一物件;  你可以將它們視為類似於將鍵對映到圖形內部資料結構中的方法。

 Graph 的邊是完全匿名的,它們僅根據端點來定義。

例項用例: Graph<Airport>, 它的邊連線機場,意味著可以直航。

ValueGraph

ValueGraph擁有Graph所做的所有與節點相關的方法,但是添加了兩個方法,用於檢索指定邊緣的值。

每個ValueGraph的邊都有一個關聯的使用者指定值。這些值不需要是唯一的 (節點必須是); ValueGraphGraph 之間的關係類似於 MapSet; 一個 Graph的邊是一組端點對, 而一個 ValueGraph的邊是從端點到值的對映。)

ValueGraph 提供 asGraph() 方法返回一個 Graph  的檢視. 這允許在Graph 例項上操作的方法也能對ValueGraph的例項起作用。

例項用例: ValueGraph<Airport, Integer>, 其邊值表示在兩個機場之間的邊連線所需的時間。

Network

Network 具有與 Graph 相關的所有與節點相關的方法,但添加了與邊和節點到邊關係一起工作的方法。例如,  outEdges(node), incidentNodes(edge), 與 edgesConnecting(nodeU, nodeV).

Network 的邊最好是唯一的物件,就像節點在所有的圖形型別中一樣。邊的唯一性約束允許Network固有地支援並行邊緣,以及關於邊和節點到邊關係的方法。

Network 提供的 asGraph() 方法能夠返回 Graph  型別的檢視. 這允許在 Graph例項上操作的方法也適用於Network例項。

例項用例: Network<Airport, Flight>, 其中邊代表可以從一個機場到達另一個機場的特定航班。

Choosing the right graph type

三種圖形型別之間的本質區別在於它們的邊表示。

Graph 具有節點之間的匿名連線的邊,沒有自身的身份或屬性。如果每對節點最多連線一條邊,並且不需要將任何資訊與邊相關聯,則應該使用Graph。

ValueGraph 具有唯一或不唯一的值(例如,邊緣權重或標籤)的邊。如果每對節點最多連線一條邊,則應該使用ValueGraph,並且需要將資訊與可能對於不同邊(例如,邊權重)相同的邊相關聯。

Network 的邊最好的唯一物件,就像節點一樣。如果邊物件是唯一的,則應該使用網路,並且希望能夠釋出引用它們的查詢。(注意,這種唯一性允許Network 支援並行邊。)

Building graph instances

common.graph 提供的實現類不是公有的,而是通過設計來實現的。This reduces the number of public types that users need to know about, and makes it easier to navigate the various capabilities that the built-implementations provide, without overwhelming users that just want to create a graph.這減少了使用者需要了解的公共型別的數量,並更加容易的使用各種功能的實現,而不會難倒只想建立圖形的使用者。

若要建立圖形型別的內建實現的一個例項,請使用相應的 Builder類:

MutableGraph<Integer> graph = GraphBuilder.undirected().build();

MutableValueGraph<City, Distance> roads = ValueGraphBuilder.directed().build();

MutableNetwork<Webpage, Link> webSnapshot = NetworkBuilder.directed()
    .allowsParallelEdges(true)
    .nodeOrder(ElementOrder.natural())
    .expectedNodeCount(100000)
    .expectedEdgeCount(1000000)
    .build();
  • 能夠獲取圖的Builder例項的方法有兩種:
    • 呼叫靜態方法 directed() 或者 undirected(). Builder提供的圖的例項可以使有向的或者無向的。
    • 呼叫靜態方法 from(), 它提供一個基於已經存在的圖的例項的 Builder .
  • 在建立好Builder例項之後,可以選擇指定該例項其他的特性和能力。
  • 可以多次呼叫 build() 方法在同一個 Builder 例項中取建立複雜的圖的例項。
  • 在Builder上不需要指定節點與邊的型別,在圖形型別本身上指定它們就足夠了。
  • build() 方法返回一個 Mutable 的子類其關聯著圖的型別, 它提供了變異方法,更多使用在下面的 "Mutable and Immutable graphs" 中.

Builder constraints(約束條件) vs. optimization hints(優化提示)

Builder 型別通常提供兩個選項: constraints and optimization hints.

在用Builder 建立例項時必須滿足指定的約束行為與屬性,例如:

  • 圖是否是有向的
  • 圖中是否允許自環
  • 圖的邊值是否是有序的

諸如此類。

優化提示可以有選擇性的由實現類用來提高效率,例如,用於確定內部資料結構的型別或初始大小。它們不能保證有任何效果。

每個圖表型別提供相應的生成器中指定的限制訪問,但不提供優化提示訪問器。

Mutable and Immutable graphs

Mutable* types

  • 增加與刪除頂點的方法:
    • addNode(node) and removeNode(node)
  • 增加與刪除邊的方法:

這與Java集合框架——以及Guava的新集合型別——歷史上的工作方式背道而馳;  這些型別中的每一種都包括用於(可選)突變方法的簽名。我們選擇部分地將可變方法分解成子型別,以支援防禦程式設計: 一般說來, 如果你得程式碼僅僅只是遍歷圖而不進行轉化,其輸入應指定為 Graph, ValueGraph, 或者 Network 而不是他們的子類。另一方面, 如果你得程式碼不需要轉變成一個object,那麼你得程式碼必須通過使用標記自己為"Mutable"來喚起人們對該事實的關注。

因此 Graph, 等是介面, 他們不包含轉變方法,但是向呼叫方提供該介面的例項不能保證呼叫方不會對其進行轉變,它不會被呼叫方轉變,因為(如果它實際上是一個Mutable*子型別),呼叫者可以把它轉換成那個子型別。 如果希望提供作為方法引數或返回值的圖不能被修改的保證,那麼應該使用Immutable實現;下面將對此進行詳細介紹。

Immutable* implementations

每一種型別的圖也包含著複雜的 Immutable 實現. 這些類與 Guava的 ImmutableSet, ImmutableList, ImmutableMap, 等相似.: 一旦被構造,則不會發生改變,並且在內部使用了高效的不可變資料結構。

不像Guava中其他的 Immutable 型別,  這些實現沒有用於突變方法的任何方法簽名,因此它們不需要為嘗試的轉變丟擲UnsupportedOperationException。

可以通過呼叫靜態的copyOf() 方法建立一個ImmutableGraph的例項:

ImmutableGraph<Integer> immutableGraph = ImmutableGraph.copyOf(graph);

Guarantees

這些Immutable* 型別做出以下保證:

  • shallow immutability: 元素永遠不能被新增,刪除或者替換(這些類也沒有實現 Mutable* 介面)
  • deterministic iteration: 迭代順序總是與輸入的圖相同。
  • integrity:  此型別不能在該包外進行子類分類(這將允許這些保證被違反)

請將這些類作為介面對待,而不是實現方法

每個Immutable*類是一個提供有意義的行為保證的型別,而不僅僅是一個特定的實現。你應該把它們當作世界上每個重要意義上的介面。

應該將儲存Immutable*例項(比如ImmutableGraph)的欄位和方法返回值宣告為Immutable*型別,而不是相應的介面型別(比如Graph)。 這傳達給呼叫方上面列出的所有語義保證,這些總是非常有用的資訊。

另一方面, ImmutableGraph的引數型別通常對呼叫方來說是討厭的。取而代之的是接受圖。

提醒: 正如在其他地方指出的,當元素包含在集合中時,修改元素(以影響其equals()行為的方式)幾乎總是個壞主意。將導致未定義的行為和bug。最好完全避免使用可變物件作為Immutable*例項的元素,因為使用者可能會期望您的“不可變”物件是深不可變的。

Graph elements (nodes and edges)

Elements must be useable as Map keys

由使用者提供的圖形元素應該被認為是由圖形實現所維護的內部資料結構的鍵。因此,用於表示圖形元素的類必須具有equals()hashCode()實現,這些實現具有或引出下面列出的屬性。

唯一性

如果 AB 滿足 A.equals(B) == true ,那麼A與B最多兩個之一是圖的元素。

hashCode()equals()之間的一致性

hashCode()必須與 equals() 所定義的 Object.hashCode()一致。

equals()與排序的一致性

如果頂點是有序的 (例如,  通過raphBuilder.orderNodes()), 那麼這個序列必須與Comparator  Comparable定義的 equals()一致。

非遞迴性

hashCode  與quals()  不能遞迴的引用其他元素, 如在本例中:

// DON'T use a class like this as a graph element (or Map key/Set element)
public final class Node<T> {
  T value;
  Set<Node<T>> successors;

  public boolean equals(Object o) {
    Node<T> other = (Node<T>) o;
    return Objects.equals(value, other.value)
        && Objects.equals(successors, other.successors);
  }

  public int hashCode() {
    return Objects.hash(value, successors);
  }
}

使用這個類作為 common.graph 元素型別 (e.g., Graph<Node<T>>) 有以下問題:

  • 冗餘: the implementations of Graph 的實現依賴common.graph提供的方法已經儲存了這些關係。
  • 效率低: 增加或存取元素需要呼叫 equals() (可能也會是 hashCode()), 時間複雜度為 O(n)
  • 不可行: 如果圖中有環, equals()hashCode() 可能不會停止。

相反,只使用T值本身作為節點型別(假設T值本身就是有效的MAP鍵)。

Elements and mutable state

如果圖元素具有可變狀態:

  • 不能在equals()/hashCode()方法中反射可變狀態(這在地圖文件中詳細討論)
  • 不要構造多個彼此相等的元素,並期望它們是可互換的。don't construct multiple elements that are equal to each other and expect them to be interchangeable. 特別地,當向圖中新增這些元素時,如果需要在建立期間多次引用這些元素(而不是向每個add*()呼叫傳遞新的MyMutableNode(id)),則應該建立它們一次,並存儲引用。

如果需要儲存可變的每個元素狀態,一個選項是使用不可變元素,並將可變狀態儲存在單獨的資料結構(例如, an element-to-state map).

Elements must be non-null

將元素新增到圖中的方法實際上是拒絕空元素的方法。

Library contracts and behaviors

本節討論 common.graph 型別的內建實現的行為。

Mutation

你可以新增一個邊,其事件節點以前沒有被新增到圖中。如果他們還沒有出現,他們會默默地新增到圖表中:

Graph<Integer> graph = GraphBuilder.directed().build();  // graph is empty
graph.putEdge(1, 2);  // this adds 1 and 2 as nodes of this graph, and puts
                      // an edge between them
if (graph.nodes().contains(1)) {  // evaluates to "true"
  ...
}

Graph equals() and graph equivalence

對於 Guava 22, common.graph 的圖型別都定義了  equals() ,這對特定型別來說是又意義的:

  • Graph.equals() 定義:兩個圖的頂點與邊的集合相同那麼這兩個如相同 (其中,兩個圖的邊具有相同的入射點與相同的方向 ).
  • ValueGraph.equals() 定義兩個  ValueGraph 如果他們具有相同的頂點與邊的集合,並且相同的邊具有相同的權值那麼這兩個圖相同。
  • Network.equals() 定義兩個 Network 如果他們具有相同的頂點與邊的集合,並且每個邊都按照相同的指向連線著相同的頂點那麼他們相同。

此外,對於每個圖型別,只有當兩個圖的邊具有相同的有向性(兩個圖都是有向的或者都是無向的)時,兩個圖才能相等。

當然,對於每個圖形型別,hashCode()總是與equals()一致。

如果僅基於連線性比較兩個網路或兩個ValueGraph或將網路或ValueGraph與Graph進行比較,則可以使用Network和ValueGraph提供的Graph檢視:

Graph<Integer> graph1, graph2;
ValueGraph<Integer, Double> valueGraph1, valueGraph2;
Network<Integer, MyEdge> network1, network2;

// compare based on nodes and node relationships only
if (graph1.equals(graph2)) { ... }
if (valueGraph1.asGraph().equals(valueGraph2.asGraph())) { ... }
if (network1.asGraph().equals(graph1.asGraph())) { ... }

// compare based on nodes, node relationships, and edge values
if (valueGraph1.equals(valueGraph2)) { ... }

// compare based on nodes, node relationships, and edge identities
if (network1.equals(network2)) { ... }

Accessor methods

Accessors which return collections:

  • 可以返回圖的檢視; 不支援對影響檢視的圖的修改(例如,在遍歷node()時呼叫addNode(n)或removeNode(n)),並且可能導致丟擲ConcurrentModificationException。
  • 如果輸入有效,但沒有元素滿足請求則返回空集合(例如:如果節點沒有相鄰節點,則相鄰節點(節點)將返回空集合)。

Accessors 將跑出 IllegalArgumentException 如果元素不在圖中.

雖然一些Java集合框架方法,如contains(),使用物件引數而不是適當的泛型型別說明符,但是Guava22,普通圖方法採用泛型型別說明符來提高型別安全性。

Synchronization

這取決於每個圖形實現來確定自己的同步策略。預設情況下,未定義的行為可能是由於在另一個執行緒正在被更改的圖上呼叫任何方法而導致的。

通常情況下,內建的可變實現不提供同步保證,但是Immutable*類(由於不可變)是執行緒安全的。

Element objects

新增到圖中的節點、邊和值物件與內建實現無關;它們只是用作內部資料結構的鍵。這意味著可以在圖例項之間共享節點/邊。

預設情況下,節點和邊緣物件是插入順序的(即,節點和邊緣物件的迭代器按照它們被新增到圖的順序訪問node()和edges(),就像LinkedHashSet一樣。

Notes for implementors

Storage models

common.graph 支援多種機制來儲存圖的拓撲結構,包括:

  • 圖形實現儲存拓撲(例如,通過儲存對映節點到相鄰節點的Map<N,Set<N>),這意味著節點只是鍵,並且可以在圖形之間共享
  • 節點儲存拓撲(例如,通過儲存相鄰節點的List<E>)
  • 單獨的資料儲存庫(例如,資料庫)儲存拓撲結構。

注意:對於支援孤立節點(沒有入射邊的節點)的圖形實現,mutiMap不是足夠的內部資料結構,因為它們限制鍵對映到至少一個值,或者在多映像中不存在。

Accessor behavior

對於返回集合的訪問器,語義有幾種選項,包括

  1. 該集合是不可變物件的拷貝 (例如 ImmutableSet): 任何嘗試改變集合的方法都將跑出異常,以及對圖的改變不會對映帶集合上來。
  2. 集合是一個不可變的檢視 (e.g. Collections.unmodifiableSet()): 任何嘗試改變集合的方法都將跑出異常,以及對圖的改變不會對映帶集合上來。
  3. 集合是一個可變的拷貝: 它可以被修改,但是對集合的修改不會反映在圖中,反之亦然。
  4. 集合是一個可變的檢視: 它可以被修改,但是對集合的修改不會反映在圖中,反之亦然。

(理論上,人們可以返回一個集合,該集合沿著一個方向而不是另一個方向進行寫操作(集合到圖或者反之亦然),但是這基本上永遠不會有用或清晰,所以請不要這樣:)

(1)和(2)通常是優選的;在本文中,內建的實現通常使用(2)。

(3)是可行的選項,但是如果使用者期望修改將影響圖表,或者對圖表的修改將反映在集合中,則可能會使使用者感到困惑。

(4)是一個危險的設計選擇,並且應該只在極端謹慎的情況下使用,因為保持內部資料結構的一致性可能很棘手。

Abstract* classes

每個圖形型別都有一個相應的 Abstract class: AbstractGraph, etc.

如果可能,圖形介面的實現者應該擴充套件適當的抽象類,而不是直接實現介面。抽象類提供了幾個關鍵方法的實現,這些方法很難正確地執行,或者具有一致的實現對其有幫助,例如:

  • *degree()
  • toString()
  • Graph.edges()
  • Network.asGraph()

Code examples

Is node in the graph?

graph.nodes().contains(node);

Is there an edge between nodes u and v (that are known to be in the graph)?

在圖是無向的情況下,下面例子中的引數u和v的排序是無關的。

// This is the preferred syntax since 23.0 for all graph types.
graphs.hasEdgeConnecting(u, v);

// These are equivalent (to each other and to the above expression).
graph.successors(u).contains(v);
graph.predecessors(v).contains(u);

// This is equivalent to the expressions above if the graph is undirected.
graph.adjacentNodes(u).contains(v);

// This works only for Networks.
!network.edgesConnecting(u, v).isEmpty();

// This works only if "network" has at most a single edge connecting u to v.
network.edgeConnecting(u, v).isPresent();  // Java 8 only
network.edgeConnectingOrNull(u, v) != null;

// These work only for ValueGraphs.
valueGraph.edgeValue(u, v).isPresent();  // Java 8 only
valueGraph.edgeValueOrDefault(u, v, null) != null;

Basic Graph example

MutableGraph<Integer> graph = GraphBuilder.directed().build();
graph.addNode(1);
graph.putEdge(2, 3);  // also adds nodes 2 and 3 if not already present

Set<Integer> successorsOfTwo = graph.successors(2); // returns {3}

graph.putEdge(2, 3);  // no effect; Graph does not support parallel edges
MutableValueGraph<Integer, Double> weightedGraph = ValueGraphBuilder.directed().build();
weightedGraph.addNode(1);
weightedGraph.putEdgeValue(2, 3, 1.5);  // also adds nodes 2 and 3 if not already present
weightedGraph.putEdgeValue(3, 5, 1.5);  // edge values (like Map values) need not be unique
...
weightedGraph.putEdgeValue(2, 3, 2.0);  // updates the value for (2,3) to 2.0

Basic Network example

MutableNetwork<Integer, String> network = NetworkBuilder.directed().build();
network.addNode(1);
network.addEdge("2->3", 2, 3);  // also adds nodes 2 and 3 if not already present

Set<Integer> successorsOfTwo = network.successors(2);  // returns {3}
Set<String> outEdgesOfTwo = network.outEdges(2);   // returns {"2->3"}

network.addEdge("2->3 too", 2, 3);  // throws; Network disallows parallel edges
                                    // by default
network.addEdge("2->3", 2, 3);  // no effect; this edge is already present
                                // and connecting these nodes in this order

Set<String> inEdgesOfFour = network.inEdges(4); // throws; node not in graph

Traversing an undirected graph node-wise

// Return all nodes reachable by traversing 2 edges starting from "node"
// (ignoring edge direction and edge weights, if any, and not including "node").
Set<N> getTwoHopNeighbors(Graph<N> graph, N node) {
  Set<N> twoHopNeighbors = new HashSet<>();
  for (N neighbor : graph.adjacentNodes(node)) {
    twoHopNeighbors.addAll(graph.adjacentNodes(neighbor));
  }
  twoHopNeighbors.remove(node);
  return twoHopNeighbors;
}

Traversing a directed graph edge-wise

// Update the shortest-path weighted distances of the successors to "node"
// in a directed Network (inner loop of Dijkstra's algorithm)
// given a known distance for {@code node} stored in a {@code Map<N, Double>},
// and a {@code Function<E, Double>} for retrieving a weight for an edge.
void updateDistancesFrom(Network<N, E> network, N node) {
  double nodeDistance = distances.get(node);
  for (E outEdge : network.outEdges(node)) {
    N target = network.target(outEdge);
    double targetDistance = nodeDistance + edgeWeights.apply(outEdge);
    if (targetDistance < distances.getOrDefault(target, Double.MAX_VALUE)) {
      distances.put(target, targetDistance);
    }
  }
}

FAQ

Why did Guava introduce common.graph?

和Guava所做的其他事情一樣同樣的論點也適用於圖:

  • 程式碼重用/互操作/正規化統一:與圖形處理相關的許多事情
  • efficiency: 使用低效率圖形表示的程式碼有多少?太多的空間(例如矩陣表示)?
  • correctness: 有多少程式碼對圖的分析是錯誤的?
  • promotion of use of graph as ADT: 如果容易的話,有多少人會使用圖表?
  • simplicity: 處理圖形的程式碼更容易理解,如果它明確地使用了那個隱喻。

What kinds of graphs does common.graph support?

This is answered in the "Capabilities" section, above.

common.graph doesn’t have feature/algorithm X, can you add it?

Maybe. You can email us at [email protected] or open an issue on GitHub.

Our philosophy is that something should only be part of Guava if (a) it fits in with Guava’s core mission and (b) there is good reason to expect that it will be reasonably widely used.

common.graph will probably never have capabilities like visualization and I/O; those would be projects unto themselves and don’t fit well with Guava’s mission.

Capabilities like traversal, filtering, or transformation are better fits, and thus more likely to be included, although ultimately we expect that other graph libraries will provide most capabilities.

Does it support very large graphs (i.e., MapReduce scale)?

Not at this time. Graphs in the low millions of nodes should be workable, but you should think of this library as analogous to the Java Collections Framework types (Map, List, Set, and so on).

Why aren't successors/predecessors/in-edges/out-edges sorted/ordered?

tl;dr: Returning sorted/ordered sets of incident edges or adjacent nodes would be quite expensive or infeasible in our implementations, since those sets are live views. That said, you could create a custom implementation of the common.graph interfaces that would have this behavior.

For more discussion, see this issue.

Why should I use it instead of something else?

tl;dr: you should use whatever works for you, but please let us know what you need if this library doesn't support it!

The main competitors to this library (for Java) are: JUNG and JGraphT.

JUNG was co-created by Joshua O'Madadhain (the common.graph lead) in 2003, and he still maintains it. JUNG is fairly mature and full-featured and is widely used, but has a lot of cruft and inefficiencies. Now that common.graph has been released externally, he is working on a new version of JUNG which uses common.graph for its data model.

JGraphT is another third-party Java graph library that’s been around for a while. We're not as familiar with it, so we can’t comment on it in detail, but it has at least some things in common with JUNG.

Rolling your own solution is sometimes the right answer if you have very specific requirements. But just as you wouldn’t normally implement your own hash table in Java (instead of using HashMap or ImmutableMap), you should consider using common.graph (or, if necessary, another existing graph library) for all the reasons listed above.

Major Contributors

common.graph has been a team effort, and we've had help from a number of people both inside and outside Google, but these are the people that have had the greatest impact.

  • Omar Darwish did a lot of the early implementations, and set the standard for the test coverage.
  • James Sexton has been the single most prolific contributor to the project and has had a significant influence on its direction and its designs. He's responsible for some of the key features, and for the efficiency of the implementations that we provide.
  • Joshua O'Madadhain started the common.graph project after reflecting on the strengths and weaknesses of JUNG, which he also helped to create. He leads the project, and has reviewed or written virtually every aspect of the design and the code.