Java tutorial
/* * Copyright (C) 2017 Baifendian Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baifendian.swordfish.common.utils.graph; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ??, , VD, ED, * "?" */ public class Graph<VK, VD, ED> { private static final Logger LOG = LoggerFactory.getLogger(Graph.class); /** * , key , value ? */ protected volatile Map<VK, VD> vertices; /** * , key , value ? */ protected volatile Map<VK, Map<VK, ED>> edges; /** * ??? */ protected volatile Map<VK, Map<VK, ED>> reverseEdges; public Graph() { vertices = new HashMap<>(); edges = new HashMap<>(); reverseEdges = new HashMap<>(); } /** * ? * * @param key id * @param vertex */ public synchronized void addVertex(VK key, VD vertex) { vertices.put(key, vertex); } /** * , ?? * * @param key id * @param vertex */ public synchronized void addVertexIfAbsent(VK key, VD vertex) { if (!containsVertex(key)) { addVertex(key, vertex); } } /** * , ?? * * @param key id */ public synchronized void removeVertex(VK key) { List<Map.Entry<VK, VK>> pairs = new ArrayList<>(); // ? for (VK postKey : getPostNode(key)) { pairs.add(new AbstractMap.SimpleEntry<>(key, postKey)); } // ? for (VK preKey : getPreNode(key)) { pairs.add(new AbstractMap.SimpleEntry<>(preKey, key)); } // for (Map.Entry<VK, VK> pair : pairs) { removeEdge(pair.getKey(), pair.getValue()); } vertices.remove(key); } /** * * * @param start * @param end * @return */ public boolean addEdge(VK start, VK end) { return addEdge(start, end, false); } /** * @param start * @param end * @param createVertex ?? * @return */ public boolean addEdge(VK start, VK end, boolean createVertex) { return addEdge(start, end, null, createVertex); } /** * , ?, , ? * * @param start * @param end * @param edge * @return */ public boolean addEdge(VK start, VK end, ED edge) { return addEdge(start, end, edge, false); } /** * , ?, ?? * * @param start * @param end * @param edge * @param createVertex ?? * @return */ public synchronized boolean addEdge(VK start, VK end, ED edge, boolean createVertex) { // ??? startId, endId ( Graph ??, DAG ?) if (!validIfAdd(start, end, createVertex)) { LOG.error("Add will be invalid, cause cycle."); return false; } addEdgeNoCheck(start, end, edge); return true; } /** * , ???, ??? * * @param start * @param end * @param edge */ private synchronized void addEdgeNoCheck(VK start, VK end, ED edge) { addVertexIfAbsent(start, null); addVertexIfAbsent(end, null); addEdge(start, end, edge, edges); addEdge(end, start, edge, reverseEdges); } /** * ? * * @param start * @param end * @param edge * @param edges ? */ private synchronized void addEdge(VK start, VK end, ED edge, Map<VK, Map<VK, ED>> edges) { edges.putIfAbsent(start, new HashMap<>()); Map<VK, ED> endEdges = edges.get(start); endEdges.put(end, edge); } /** * * * @param start * @param end */ public synchronized void removeEdge(VK start, VK end) { removeEdge(start, end, edges); removeEdge(end, start, reverseEdges); } /** * * * @param start * @param end * @param edges */ private synchronized void removeEdge(VK start, VK end, Map<VK, Map<VK, ED>> edges) { Map<VK, ED> endEdges = edges.get(start); if (endEdges != null) { endEdges.remove(end); // ?, , if (endEdges.isEmpty()) { edges.remove(start); } } } /** * endId ?, ? endId , addStartIds. ????, ?. */ public boolean forceRefreshPreEdges(Collection<VK> addStartKeys, VK endKey) { return forceRefreshPreEdges(addStartKeys, endKey, false); } /** * @param addStartKeys * @param endKey * @param createVertex * @return */ public synchronized boolean forceRefreshPreEdges(Collection<VK> addStartKeys, VK endKey, boolean createVertex) { List<VK> l = new ArrayList<>(); l.addAll(getPreNode(endKey)); return addRemoveMultiEdges(l, addStartKeys, endKey, createVertex); } /** * , ?, ? * * @param removeStartKeys * @param addStartKeys * @param endKey * @return */ public boolean addRemoveMultiEdges(Collection<VK> removeStartKeys, Collection<VK> addStartKeys, VK endKey) { return addRemoveMultiEdges(removeStartKeys, addStartKeys, endKey, false); } /** * , ?, ?? * * @param removeStartKeys * @param addStartKeys * @param endKey * @param createVertex ?, ? * @return */ public synchronized boolean addRemoveMultiEdges(Collection<VK> removeStartKeys, Collection<VK> addStartKeys, VK endKey, boolean createVertex) { for (VK startKey : addStartKeys) { /** * ?, ??, false. * ?, ?, ? endKey, ??. * ??, , : * 1) ??; * 2) ? startKey{i} -> endKey , ? startKey{i} ; * 3) ? startKey{i} -> endKey , ? startKey{i} -> endKey . */ if (!validIfAdd(startKey, endKey, createVertex)) { return false; } } // for (VK startKey : removeStartKeys) { removeEdge(startKey, endKey); } // for (VK startKey : addStartKeys) { // ? addEdgeNoCheck(startKey, endKey, null); } return true; } /** * */ public synchronized void clear() { vertices.clear(); edges.clear(); reverseEdges.clear(); } /** * ?? * * @param key id * @return */ public synchronized boolean containsVertex(VK key) { return vertices.containsKey(key); } /** * ? * * @param key id * @return */ public synchronized VD getVertex(VK key) { return vertices.get(key); } /** * * * @return */ public Map<VK, VD> getVertices() { return vertices; } /** * ?? * * @param startKey * @param endKey * @return */ public synchronized boolean containsEdge(VK startKey, VK endKey) { Map<VK, ED> endEdges = edges.get(startKey); if (endEdges == null) { return false; } return endEdges.containsKey(endKey); } /** * * * @return */ public Map<VK, Map<VK, ED>> getEdges() { return edges; } /** * ? * * @param startKey * @param endKey * @return */ public synchronized ED getEdge(VK startKey, VK endKey) { Map<VK, ED> endEdges = edges.get(startKey); if (endEdges == null) { return null; } return endEdges.get(endKey); } /** * ? * * @return */ public synchronized int getEdgeNumber() { int c = 0; for (Map.Entry<VK, Map<VK, ED>> entry : edges.entrySet()) { c += entry.getValue().size(); } return c; } /** * ? * * @return */ public int getVertexNumber() { return vertices.size(); } /** * ? * * @return */ public synchronized Collection<VK> getStartVertex() { return CollectionUtils.subtract(vertices.keySet(), reverseEdges.keySet()); } /** * ? * * @return */ public synchronized Collection<VK> getEndVertex() { return CollectionUtils.subtract(vertices.keySet(), edges.keySet()); } /** * ?? * * @param key id * @return */ public Set<VK> getPreNode(VK key) { return getNeighborNode(key, reverseEdges); } /** * ??? * * @param key id * @return */ public Map<VK, ED> getPreNodeAttr(VK key) { return getNeighborNodeAttr(key, reverseEdges); } /** * ?? * * @param key id * @return */ public Set<VK> getPostNode(VK key) { return getNeighborNode(key, edges); } /** * ??? * * @param key id * @return */ public Map<VK, ED> getPostNodeAttr(VK key) { return getNeighborNodeAttr(key, edges); } /** * ? * * @param key id * @param edges ? * @return */ private Set<VK> getNeighborNode(VK key, final Map<VK, Map<VK, ED>> edges) { return getNeighborNodeAttr(key, edges).keySet(); } /** * ?? * * @param key id * @param edges * @return */ private synchronized Map<VK, ED> getNeighborNodeAttr(VK key, final Map<VK, Map<VK, ED>> edges) { final Map<VK, ED> neighborEdges = edges.get(key); if (neighborEdges == null) { return Collections.EMPTY_MAP; } return neighborEdges; } /** * ? id * * @param key id * @return */ public synchronized int getIndegree(VK key) { Collection<VK> getNeighborNode = getPreNode(key); if (getNeighborNode == null) { return 0; } return getNeighborNode.size(); } /** * id * * @param key id * @return */ public synchronized int getOutdegree(VK key) { Collection<VK> getNeighborNode = getPostNode(key); if (getNeighborNode == null) { return 0; } return getNeighborNode.size(); } /** * startId -> endId ??, ?? * * @param startKey * @param endKey * @param createVertex ? * @return */ protected synchronized boolean validIfAdd(VK startKey, VK endKey, boolean createVertex) { // ?? if (startKey.equals(endKey)) { LOG.error("Edge start can't equals to end ."); return false; } if (!createVertex) { return containsVertex(startKey) && containsVertex(endKey); } return true; } /** * ? * * @return true if has cycle, else return false. */ public boolean hasCycle() { return !topologicalSortImpl().getKey(); } /** * ??, ???, * * @return * @throws Exception */ public List<VK> broadFirstSearch() throws Exception { List<VK> visit = new ArrayList<>(); Queue<VK> q = new LinkedList<>(); Set<VK> hasVisited = new HashSet<>(); synchronized (this) { // for (VK key : getStartVertex()) { q.add(key); hasVisited.add(key); visit.add(key); } while (!q.isEmpty()) { VK key = q.poll(); // ? for (VK postKey : getPostNode(key)) { if (!hasVisited.contains(postKey)) { q.add(postKey); hasVisited.add(postKey); visit.add(postKey); } } } // ?? if (visit.size() != getVertexNumber()) { throw new Exception("Broad first search can't search complete."); } } return visit; } /** * ??, ???, * * @return * @throws Exception */ public List<VK> depthFirstSearch() throws Exception { List<VK> visit = new ArrayList<>(); Set<VK> hasVisited = new HashSet<>(); synchronized (this) { for (VK key : getStartVertex()) { depthFirstSearch(key, visit, hasVisited); } // ?? if (visit.size() != getVertexNumber()) { throw new Exception("Depth first search can't search complete."); } } return visit; } /** * ?? * * @param key ?? id * @param visit * @param hasVisited ?? */ private void depthFirstSearch(VK key, Collection<VK> visit, Set<VK> hasVisited) { visit.add(key); hasVisited.add(key); for (VK postKey : getPostNode(key)) { if (!hasVisited.contains(postKey)) { depthFirstSearch(postKey, visit, hasVisited); } } } /** * topological ?, , * * @return * @throws Exception */ public List<VK> topologicalSort() throws Exception { Map.Entry<Boolean, List<VK>> entry = topologicalSortImpl(); if (entry.getKey()) { return entry.getValue(); } throw new Exception("Graph has a cycle, can't compute topological sort."); } /** * topological ? * * @return key ?, ? true, () false, value topology sort */ private Map.Entry<Boolean, List<VK>> topologicalSortImpl() { List<VK> sort = new ArrayList<>(); Queue<VK> zeroVertex = new LinkedList<>(); Map<VK, Integer> indegrees = new HashMap<>(); synchronized (this) { // ? vertex , ?? for (Map.Entry<VK, VD> id2Vertex : vertices.entrySet()) { VK key = id2Vertex.getKey(); int inDegree = getIndegree(key); if (inDegree == 0) { sort.add(key); zeroVertex.add(key); } else { indegrees.put(key, inDegree); } } // topology , 0 , ?? while (!zeroVertex.isEmpty()) { VK key = zeroVertex.poll(); Collection<VK> postNodes = getPostNode(key); for (VK postKey : postNodes) { int d = indegrees.getOrDefault(postKey, 0); if (d <= 1) { sort.add(postKey); indegrees.remove(postKey); zeroVertex.add(postKey); } else { indegrees.put(postKey, d - 1); } } } } // indegrees , , ? return new AbstractMap.SimpleEntry(indegrees.isEmpty(), sort); } /** * ??, ???, ??: * 1) , ????? * 2) ????, , ? * * @return true , false ? */ public synchronized boolean isConnected() { Queue<VK> q = new LinkedList<>(); Set<VK> hasVisited = new HashSet<>(); // ? Iterator<Map.Entry<VK, VD>> iter = vertices.entrySet().iterator(); // , true if (!iter.hasNext()) { return true; } // ???? Map.Entry<VK, VD> entry = iter.next(); VK startKey = entry.getKey(); q.add(startKey); hasVisited.add(startKey); while (!q.isEmpty()) { VK key = q.poll(); for (VK postKey : getPostNode(key)) { if (!hasVisited.contains(postKey)) { q.add(postKey); hasVisited.add(postKey); } } for (VK preKey : getPreNode(key)) { if (!hasVisited.contains(preKey)) { q.add(preKey); hasVisited.add(preKey); } } } return hasVisited.size() == getVertexNumber(); } }