圖論(2):Guava中Graph模塊(wiki翻譯)

背景介紹

由於Graph模塊直到最近幾個版本才加入到Guava中,網上對應的中文教程也幾乎是缺失的,因此想借機翻譯下它對應wiki的文檔,以此作爲入門示例。文章肯定有一些翻譯不恰當的地方,會在一步一步的瞭解其理念後,漸進的更新註釋。
對應的原文文檔爲:https://github.com/google/guava/wiki/GraphsExplained
爲了便於理解,先拋出它的架構類圖,在心目中有個大概的輪廓:

圖論的解釋

Guava庫的目錄common.graph包含的模塊是一個描述實體(entity)以及實體之間的關係的圖數據結構模型庫。例如:網頁與超鏈接、科學家與他們寫的論文、機場與其航線、人與其家族等。Guava-Graph模塊的目的是提供一種通用以及可擴展的語言來描述類似上述的舉例。


定義(Definitions)

圖中包含一組節點(node)(也稱爲頂點)和一組連接節點的(edge)(也稱爲鏈接或者弧);邊緣的節點我們稱爲端點(endpoint)。(我們將在下文介紹一個叫Graph的接口,我們將使用“圖”來作爲一般術語描述該數據結構,也可以使用它引用某個具體的圖類型,即接口Graph有很多具體的實現類)。
如果一條邊定義了開始(source)和結束(target),這條邊被城爲有向邊(directed),否則稱爲無向邊(undirected)。有向邊適用於非對稱的關係模型(起源、指向、作者),而無向邊適用於對稱關係模型(摺疊、距離、同級關係)。
圖中每一條邊都是有向邊的,則被稱爲有向圖;每一條邊都是無向的,則被稱爲無向圖。(common.graph模塊不支持圖中既有有向邊又有無向邊的情形。)
舉例:

graph.addEdge(nodeU, nodeV, edgeUV);
  • nodeUnodeV是兩個鄰接點(adjacent)。
  • edgeUV是頂點nodeU到頂點nodeV的事件(incident)(反之亦然)

有向圖中,有如下定義:

  • nodeUnodeV的一個前趨(predecessor)
  • nodeVnodeU的一個後繼(successor)
  • edgeUVnodeU的一條出度(outgoing)
  • edgeUVnodeV的一條入度(incoming)
  • nodeU是邊edgeUV起點(source)
  • nodeV是邊edgeUV終點(target)

無向圖中,有如下定義:

  • nodeU既是nodeV的前趨也是nodeV的後繼
  • nodeV既是nodeU的前趨也是nodeU的後繼
  • edgeUV既是nodeU的入度也是nodeU的出度
  • edgeUV既是nodeV的入度也是nodeV的出度

一條連接節點本身的邊被稱爲自環(self-loop),也就是說,一條邊連接了兩個相同的節點。如果這個自環是有向的,那麼這條邊既是節點的入度邊也是節點的出度邊,這個節點既是邊的起點(source)也是邊的終點(target)。

如果兩條邊以相同的順序連接相同的節點,則稱這兩條邊爲平行邊(parallel);如果以相反的順序連接相同的節點則稱這兩條邊爲逆平行邊(antiparallel)。(無向邊不能被稱爲逆平行邊)

例如:

//有向圖
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_a和邊edgeUV_b是相互平行邊,與邊edgeVU是逆平行邊;
在無向圖undirectedGraph中,邊edgeUV_aedgeUV_bedgeVU是兩兩相互逆平行邊。


功能(Capabilities)

common.graph模塊的核心是提供圖相關操作的接口。另外,它沒有提供類似I/O或者可視化的功能。如果選用這個模塊將會有非常多的限制,具體詳細信息可以查看下面FAQ的相關主題。總體來講,它提供瞭如下幾種類型的圖:

  • 有向圖
  • 無向圖
  • 節點和(或)邊帶權圖
  • 允許(不允許)自環圖
  • 允許(不允許)平行邊圖(允許平行邊圖有時也稱爲多重圖(multigraphs)
  • 節點或邊被有序插入、順序、無序圖(graphs whose nodes/edges are insertion-ordered, sorted, or unordered)

Javadoc中有這樣的描述:common.graph中的各種類型的圖都是通過與其相關的Builder具體實現類型來構建的,不過這些Builder實現類型不一定支持上面提到的所有圖類型,但也可能支持其他類型的圖。
庫中圖的數據結構是通過矩陣鄰接list鄰接map等方式來存儲的,選擇何種存儲方式取決於適用的實現場景。

對於以下這些變形圖在common.graph中沒有確切的支持,儘管它們可以通過已有的圖類型進行建模:

  • 樹(trees)、森林(forests)
  • 由不同類型的同類元素(節點)構成的圖。(二分圖/k分圖、multimodal graphs
  • 超圖

common.graph不允許圖中同時存在有向邊和無向邊。Graphs中提供了很多基本操作(如:圖的拷貝和比較操作)。


圖的類型

common.graph模塊中有三種通過來作爲區分依據的"top-level"接口(interface):GraphValueGraph、和Network。另外還存在一些同級類型,不過這些都不是這三種類型的子類型。
上面三種 "top-level" 接口都繼承自接口SuccessorsFunctionPredecessorsFunction。這樣做是爲了在僅需要訪問節點的後繼(successors)或者前趨(predecessors)的圖中,它可以直接被用來作爲圖算法(例如,BFS廣度優遍歷)中參數的類型。 This is especially useful in cases where the owner of a graph already has a representation that works for them and doesn't particularly want to serialize their representation into a common.graph type just to run one graph algorithm.(不知道怎麼翻)

Graph

Graph是最簡單也是最基本的圖類型。爲了處理節點與節點之間的關係它定義了一些基本的操作,例如:successors(node)-->獲取node的後繼、adjacentNodes(node)-->獲取node的鄰接點、inDegree(node)-->獲取node的入度等。這些節點在圖中都是唯一的對象,在其內部數據結構中,你可以認爲它們是Map的鍵值(Key)。

Graph中的邊是完全匿名的,他們只能根據端點來定義。舉例:Graph<Airport>中,其邊連接任意兩個可以直航的機場。

ValueGraph

接口ValueGraph包含了Graph中的所有與節點相關的方法,並增加了一些檢索指定邊權值的方法。
ValueGraph中的每一條邊都有一個與之相關的特定權值,但是這些權值不能保證唯一性。ValueGraphGraph的關係類似與MapSet的關係(Graph中的邊是以頂點對的形式保存在Set中,而ValueGraph的邊是以頂點對與其權值的映射關係保存在Map中)。
ValueGraph提供了一個asGraph()的函數,它可以從ValueGraph中返回一個Graph視圖,這樣作用於Graph實例上的方法也能作用於ValueGraph的實例上。
舉例:ValueGraph<Airport, Integer>,其邊表示在能直航的兩個機場之間航班必須花費的時間。

Network

Network中包含了Graph中的所有與節點相關的方法,還增加了操作邊以及操作頂點與邊的關係的方法,例如:outEdges(node)--->獲取node的出度邊, incidentNodes(edge)-->獲取邊edge的頂點對, 和 edgesConnecting(nodeU, nodeV)-->獲取nodeUnodeV的直連邊。
Network中每一條邊都是唯一的,就像節點在所有的Graph類型中是唯一的一樣。邊的唯一性限制使得Network能夠天然的支持並行邊,以及與邊和節點與邊相關的方法。
Network類提供了一個asGraph()的方法,它可以從Network中返回一個Graph視圖,這樣作用於Graph實例上的方法也能操作Network的實例上。
舉例:Network<Airport, Flight>,它的每一條邊代表了從一個機場到另一個機場可以乘坐的特定航班(兩個機場之間可以同時有多趟航班)。

如何選擇合適的圖類型

這三種圖類型之間本質的區別在於它們邊表示的不同:

  • Graph中的邊是節點之間的匿名連接,沒有自己的標識或屬性。如果每一對節點之間都是通過最多一條邊連接的,而且這些邊沒有任何與之相關的信息時,則選用它。
  • ValueGraph中的邊帶有一個值(例如權值或標籤),且邊在整個圖中不能保證其唯一性。如果每一對節點之間都是通過最多一條邊連接的,並且每一條邊都有與之相關的權值時,則選用它。
  • Network中邊是全局唯一的,就像節點在圖中是唯一的一樣。如果邊對象需要唯一,並且希望能查詢它們的引用時,則選用它。(請注意,這種唯一性使得Network支持平行邊。)

構建圖的實例

common.graph中圖的具體實現類並沒有設計成public的,這樣主要是爲了減少用戶需要了解的圖類型的數量,使得構建各種功能的圖變得更加容易。
要創建一個內置圖類型的實例,可以使用相應的Builder類: GraphBuilderValueGraphBuilderNetworkBuilder。例如:

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來構建圖實例:
    1、調用靜態方法directed() 或者undirected()來實例化一個有向圖或者無向圖。
    2、調用靜態方法from()基於一個已存在的圖實例構建圖。
  • 在創建了Builder實例後,還可以選擇指定其他特性和功能。
  • 同一個Builder實例可以多次調用build()方法來創建多個圖的實例。
  • 不需要在Builder上指定節點和邊的類型,只需要在圖類型本身上指定即可。
  • build()方法返回一個Mutable子類型的圖時,提供了變形的方法。下面將會介紹更多關於MutableImmutable的圖。

Builder constraints vs. optimization hints

The Builder types generally provide two types of options: constraints and optimization hints.

Constraints specify behaviors and properties that graphs created by a given Builder instance must satisfy, such as:

  • whether the graph is directed
  • whether this graph allows self-loops
  • whether this graph's edges are sorted

and so forth.

Optimization hints may optionally be used by the implementation class to increase efficiency, for example, to determine the type or initial size of internal data structures. They are not guaranteed to have any effect.

Each graph type provides accessors corresponding to its Builder-specified constraints, but does not provide accessors for optimization hints.

可變(Mutable)和不可變(Immutable)圖

Mutablexx類型

每種圖類型都有與其對應的Mutablexx子類型: MutableGraphMutableValueGraph,以及 MutableNetwork。這些子類型定義了下面這些變形方法:

  • 增加和刪除節點的方法:addNode(node)removeNode(node)
  • 增加和刪除邊的方法:
    MutableGraph-->putEdge(nodeU, nodeV)removeEdge(nodeU, nodeV)
    MutableValueGraph-->putEdgeValue(nodeU, nodeV, value)removeEdge(nodeU, nodeV)
    MutableNetwork-->addEdge(nodeU, nodeV, edge)removeEdge(edge)
    這些方法的定義與java的集合類型以及guava的新集合類型都有所不同——每種類型都包含變形方法的函數簽名。選擇將變形方法放在子類型中,一部分原因是爲了鼓勵防禦性編程:一般來說,如果你的代碼只是爲了檢查或者遍歷一個圖,而不改變它,那麼輸入應該就指定爲GraphValueGraph或者 Network類型,而不是它們的可變子類型。另一方面,如果你的代碼確實需要修改一個對象,那麼使用帶Mutable修飾的子類對你會很有幫助。
    由於Grpah等都是接口,即使它們不包含這些變形方法,但這些接口的實例並不能保證這些方法不被調用(如果它們實際上是Mutablexx的子類型),因爲調用者可能會將其轉換成該子類型。如果有一種契約的保證,即一個方法的參數或返回值不能被修改的Grahp類型,你可以使用下面介紹的這種不可變(Immutable)實現。

Immutablexx的實現

每一種圖類型(GraphValueGraphNetwork)都有相應的不可變實現類(ImmutableGraphImmutableValueGraphImmutableNetwork),這些類類似於Guava的ImmutableSetImmutableListImmutableMap等,一旦被創建出來,它們就不能被修改,且它們在內部使用了高效的不可變數據結構。
不同與Guava的其他不可變類型,這些不可變實現類壓根沒有提供修改的方法,所以他們不需要拋出UnsupportedOperationException異常來應對這些操作。
通過調用靜態方法copyOf()來創建ImmutableGraph等的實例。例如:

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

每一種Immutable*類型提供了一下保證:

  • 不變性:元素不能被添加、刪除、替換。(這些類沒有實現Mutable*的接口)
  • 迭代的確定性:迭代順序總是和輸入圖的順序相同
  • 線程安全:多個線程同時訪問該圖是安全的操作
  • 完整性:該類型不能在包之外定義子類型(允許違反這條)

將這些類看作成接口(interface),而不是實現類:
每一個Immutable*類都是提供了有意義行爲保證的類型,而不僅僅是特定的實現類。所以你應該把它們當作是有重要意義的接口來看待。
如果字段或返回值是Immutable*的實例(如ImmutableGraph),則應將其申明爲Immutable*類型,而不是對應的接口類型(如Graph)。This communicates to callers all of the semantic guarantees listed above, which is almost always very useful information.
另一方面,一個ImmutableGraph類型的參數對調用者來說會覺得比較麻煩,而更傾向與Graph類型。

警告:正如其他地方指出的,修改一個包含在集合中的元素幾乎總是一個壞注意,這樣會導致一些未定義的行爲和錯誤出現。因此,通常最好避免使用可變對象作爲Immutable*類型的對象的元素,因爲大多數用戶是期望你的immutable對象是真的不可修改。

圖的元素(節點和邊)

節點(Network中的邊)必須可以用做Map的鍵

  • 必須在圖中唯一:當且僅當nodeA.equals(nodeB) == false時,則認爲nodeAnodeB是不相等的。
  • 必須合適的實現函數equals()hashCode()
  • 如果元素是有序的(例如GraphBuilder.orderNodes()),則必須和equals()保持一致(由ComparatorComparable接口定義)

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

  • 不能在equals()/ hashCode()方法中反射獲取可變狀態(這些在Map的相關文檔中有過詳細討論)
  • 不要創建多個相等的元素,並希望它們可以互換。特別是,如果你需要在創建期間多次引用這些元素,應該在向圖中添加這些元素時一次性的創建並保存其引用(而不是每次都通過new MyMutableNode(id)傳給add**()的中)

如果需要保存可變元素的每一個可變狀態,可以選用不可變元素,並將可變狀態保存在單獨的數據結構中(如:一個元素狀態Map)。

圖的元素必須不能爲null!


common.graph的契約和行爲

本節討論常見內置圖的實現方式。

變形

可以往圖中添加一條邊,如果圖中還沒有出現對應的兩個端點時,兩個端點則會靜默添加到圖中:

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"
  ...
}

圖的equals()和等價

Guava的22版本中每種圖都定義有特定意義的equals():

  • Graph.equals()定義爲兩個相等的Graph有相同的節點和邊集(也就是說,兩個Graph中,每條邊有相同的端點以及相同的方向)。
  • ValueGraph.equals()定義爲兩個相等的ValueGraph有相同的節點和邊集,並且邊上的權重也相等。
  • Network.equals()定義爲兩個相等的Network有相同的節點和邊集,以及每個邊對象都以相同的方向連接相同的節點(如果有邊的話)。

此外,對於每種圖類型,只有它們的邊具有相同的方向時,兩個圖才能是相等的(兩個圖要麼都是有向的,要麼都是無向的)。
理所當然,hashCode()函數在每種圖類型中都與equals()保持一致。
如果想比較兩個Network或者兩個基於連接性的ValueGraph,或者將一個Network或一個ValueGraphGraph進行比較,可以使用NetworkValueGraph提供的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)) { ... }

訪問器方法

訪問器可以返回下面兩種集合:

  • 返回圖的視圖。不支持對圖的修改將結果反映到到視圖上(例如,在通過nodes()遍歷圖時,又在調用addNode(n)或者removeNode(n)),而且可能會拋出ConcurrentModificationException異常。
  • 儘管輸入是有效的,但是當沒有元素滿足請求時,則會返回一個空集合。

如果傳遞的元素不在圖中,訪問器將會拋出IllegalArgumentException異常。

一些JDK的集合框架中的方法(如contains())使用對象來作爲參數,而沒有使用合適的泛型參數;在Guava22中common.graph模塊中方法一般採用泛型參數來改進類型的安全性。

同步

圖的同步策略取決於每個圖自己的實現。默認情況下,如果在另一個線程中修改了圖,則調用圖的任何方法都可能導致未定義的行爲。
一般來說,內置的實現的可變類型沒有提供同步的保證,但是Immutable*類型(由於是不可修改的)是線程安全的。

元素對象

添加到圖中的節點、邊或值對象與內置實現無關,它們只是作爲內部數據結構的鍵。這意味這,節點/邊可以在圖實例之間共享。
默認情況下,節點和邊是按照順序來插入的(也就是說,nodes()edges()Iterator是按照它們加入到圖中的順序的順序來訪問的,就像在LinkedHashSet中一樣)。


實現者的筆記

存儲模型

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

  • 圖本身自己實現拓撲存儲(例如使用Map<N, Set<N>>存儲,將節點映射到鄰接節點上),這意味着節點只是鍵,可以在不同的圖之間共享。
  • 節點自己存儲拓撲順序(例如List<E>存儲的鄰接節點),這意味着節點獨立與圖。
  • 一個單獨的數據存儲庫(如數據庫)存儲拓撲順序。

注意:Multimap不是一個支持孤立節點(沒有邊的節點)的內部數據結構,由於它們的限制,一個鍵要麼至少映射一個值,要麼不出現在Multimap中。

訪問行爲

對於返回一個集合的訪問器函數,有下面幾個語義選項:

  1. 集合是一個不可變拷貝(例如:ImmutableSet):試圖以任何方式修改集合的嘗試都將拋出異常,且對圖的修改不會反映到集合中
  2. 集合是一個不可修改的視圖(例如:Collections.unmodifiableSet()):視圖以任何方式修改集合的嘗試都將拋出異常,且對圖的修改將反映到集合中
  3. 集合是一個可變拷貝:它可以被修改,但是對圖的修改不會反映到集合中,反之亦然
  4. 集合是一個可變的視圖:它可以被修改,對集合的修改也將反映到圖中,反之亦然

(理論上,你可以返回一個through writes in one direction but not the other (collection-to-graph or vice-versa)的集合,但是基本上不會有用或者邏輯清晰,所以請不要這樣做)

(1)和(2)一般是首選,在撰寫本文時,內置的實現通常使用(2);(3)是一個可行的選項,但是如果用戶期望修改集合影響圖,或者對圖的修改反映在集合上,可能會讓用戶感到困惑;(4)是一個非常危險的設計選擇,應該非常謹慎的使用,因爲這樣很難保證內部數據結構的一致性。

Abstract*類

每一種圖都有一個相應的Abstract類:AbstractGraph等。
如果可能的話,圖的實現應該繼承適當的Abstract類,而不是直接實現接口。Abstract類提供了幾個比較難正確執行或者有助於實現一致性實現的方法,例如:

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

代碼例子

判斷節點是否在圖中?

graph.nodes().contains(node);

節點u和節點v是否存在邊?

在無向圖的情況下,下面例子中的參數uv是順序無關的。

// 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;

Graph例子

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

ValueGraph例子

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

Network例子

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

遍歷無向圖

// 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;
} 

遍歷有向圖

// 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

爲什麼Guava要介紹common.graph

因爲Guava所做的其他事情,同樣的理論也適用於圖:

  • 代碼重用/互操作性的統一範式:很多事情的處理都與圖相關
  • 效率:有多少代碼使用了低效的、佔用更多空間的圖結構(例如矩陣表示)?
  • 正確性:做圖分析的代碼有多少錯誤?
  • 推廣圖的抽象數據類型:如果圖更容易使用,會有多少人會使用圖?
  • 簡潔性:code which deals with graphs is easier to understand if it’s explicitly using that metaphor.

common.graph都支持哪些類型的圖?

上面的“功能”章節已經回答這個了。

common.graph沒有 feature/algorithm X,後面會添加嗎?

也許可能。
我們的理念是,如果(a)與Guava的核心使命相適應,那麼就應該有這些東西;(b)有充分的理由期待它會被合理地廣泛使用。
comon.graph可能永遠都不會有像可視化或者I/O這樣的功能,因爲它們本身就可以成爲一個項目,與Guava的任務不太相符。
像遍歷(traversal)、過濾(filtering)或者轉換(transformation)這樣的功能更加合適,因此將更有可能被包含在庫中,儘管最終我們希望其他的圖庫能提供這些功能。

是否支持非常大的圖(例如MapReduce)?

這一次還不是。在少於百萬個節點的圖應該還是可行的,但你應該把這個庫看成類似Java集合框架類型(MapListSet等等)。

爲什麼我要使用它而不是其他的庫?

你應該使用適合你的方法,但是如果這個庫不支持你,請告訴我們你需要什麼。
這個庫的主要競品有:JUNGJGraphT

  • JUNG是Joshua O'Madadhain在2003年開源的(領先與common.graph),直到現在他還在維護它。JUNG是相當成熟和功能齊全的圖庫,已經被廣泛使用,不過它仍有些晦澀而且低效。現在common.graph已經發布了,它計劃創建一個新版本的JUNG
  • JGraphT是另一個已經存在一段時間的第三方Java版的圖庫。我們不太熟悉它,所以我們不能詳細評價它,但它至少與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.


主要貢獻者

common.graph是一個團隊的努力,我們獲得了來自谷歌內外許多人的幫助,只是下面這些人的影響最大:

  • Omar Darwish做了很多早期的實現,併爲覆蓋測試設置了標準。
  • James Sexton是該項目貢獻最多的人,他對項目的方向和設計產生了重大的影響;他負責一些關鍵性特性也負責我們所提供這些實現上的有效性。
  • Joshua O'Madadhain在反思JUNG的優點和缺點後,在他的幫助下我們啓動了common.graph項目,他領導了這個項目的開展,並對設計和代碼的各個方面進行了review和編寫。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章