Java tutorial
/** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package eu.itesla_project.graph; import com.google.common.base.Function; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import eu.itesla_project.commons.ITeslaException; import gnu.trove.list.array.TIntArrayList; import gnu.trove.list.linked.TIntLinkedList; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * * @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com> */ public class UndirectedGraphImpl<V, E> implements UndirectedGraph<V, E> { private static final int VERTICES_CAPACITY = 10; private static final int EDGES_CAPACITY = 15; private static final int NEIGHBORS_CAPACITY = 2; private static class Vertex<E> { private E object; public E getObject() { return object; } public void setObject(E object) { this.object = object; } } private static class Edge<E> { private final int v1; private final int v2; private E object; private Edge(int v1, int v2, E object) { this.v1 = v1; this.v2 = v2; this.object = object; } public int getV1() { return v1; } public int getV2() { return v2; } public E getObject() { return object; } public void setObject(E object) { this.object = object; } @Override public String toString() { return object.toString(); } } /* vertices */ private final List<Vertex<V>> vertices = new ArrayList<>(VERTICES_CAPACITY); /* edges */ private final List<Edge<E>> edges = new ArrayList<>(EDGES_CAPACITY); /* cached adjacency list */ private TIntArrayList[] adjacencyListCache; private final Lock adjacencyListCacheLock = new ReentrantLock(); private final TIntLinkedList removedVertices = new TIntLinkedList(); private final TIntLinkedList removedEdges = new TIntLinkedList(); private final List<UndirectedGraphListener> listeners = new CopyOnWriteArrayList<>(); public UndirectedGraphImpl() { } private void checkVertex(int v) { if (v < 0 || v >= vertices.size() || vertices.get(v) == null) { throw new ITeslaException("Vertex " + v + " not found"); } } private void checkEdge(int e) { if (e < 0 || e >= edges.size() || edges.get(e) == null) { throw new ITeslaException("Edge " + e + " not found"); } } @Override public int addVertex() { int v; if (removedVertices.isEmpty()) { v = vertices.size(); vertices.add(new Vertex<V>()); } else { v = removedVertices.removeAt(0); } invalidateAdjacencyList(); notifyListener(); return v; } @Override public V removeVertex(int v) { checkVertex(v); for (Edge<E> e : edges) { if (e.getV1() == v || e.getV2() == v) { throw new RuntimeException("An edge is connected to vertex " + v); } } V obj = vertices.get(v).getObject(); if (v == vertices.size() - 1) { vertices.remove((int) v); } else { vertices.set(v, null); removedVertices.add(v); } invalidateAdjacencyList(); notifyListener(); return obj; } @Override public int getVertexCount() { return vertices.size() - removedVertices.size(); } @Override public void removeAllVertices() { if (edges.size() > 0) { throw new RuntimeException("Cannot remove all vertices because there is still some edges in the graph"); } vertices.clear(); invalidateAdjacencyList(); notifyListener(); } @Override public int addEdge(int v1, int v2, E obj) { checkVertex(v1); checkVertex(v2); int e; Edge<E> edge = new Edge<>(v1, v2, obj); if (removedEdges.isEmpty()) { e = edges.size(); edges.add(edge); } else { e = removedEdges.removeAt(0); edges.set(e, edge); } invalidateAdjacencyList(); notifyListener(); return e; } @Override public E removeEdge(int e) { checkEdge(e); E obj = edges.get(e).getObject(); if (e == edges.size() - 1) { edges.remove((int) e); } else { edges.set(e, null); removedEdges.add(e); } invalidateAdjacencyList(); notifyListener(); return obj; } @Override public void removeAllEdges() { edges.clear(); removedEdges.clear(); invalidateAdjacencyList(); notifyListener(); } @Override public int getEdgeCount() { return edges.size() - removedEdges.size(); } @Override public int[] getVertices() { TIntArrayList t = new TIntArrayList(vertices.size()); for (int i = 0; i < vertices.size(); i++) { if (vertices.get(i) != null) { t.add(i); } } return t.toArray(); } @Override public int getMaxVertex() { return vertices.size(); } @Override public Iterable<V> getVerticesObj() { return FluentIterable.from(vertices).filter(Predicates.notNull()).transform(Vertex::getObject); } @Override public V getVertexObject(int v) { checkVertex(v); return vertices.get(v).getObject(); } @Override public void setVertexObject(int v, V obj) { checkVertex(v); vertices.get(v).setObject(obj); } @Override public int getEdgeVertex1(int e) { checkEdge(e); return edges.get(e).getV1(); } @Override public int getEdgeVertex2(int e) { checkEdge(e); return edges.get(e).getV2(); } @Override public Iterable<E> getEdgesObject() { return FluentIterable.from(edges).filter(Predicates.notNull()).transform(Edge::getObject); } @Override public E getEdgeObject(int e) { checkEdge(e); return edges.get(e).getObject(); } @Override public List<E> getEdgeObjects(int v1, int v2) { checkVertex(v1); checkVertex(v2); List<E> edgeObjects = new ArrayList<>(1); TIntArrayList[] adjacencyList = getAdjacencyList(); TIntArrayList adjacentEdges = adjacencyList[v1]; for (int i = 0; i < adjacentEdges.size(); i++) { int e = adjacentEdges.getQuick(i); Edge<E> edge = edges.get(e); if ((edge.getV1() == v1 && edge.getV2() == v2) || (edge.getV1() == v2 && edge.getV2() == v1)) { edgeObjects.add(edge.getObject()); } } return edgeObjects; } private TIntArrayList[] getAdjacencyList() { adjacencyListCacheLock.lock(); try { if (adjacencyListCache == null) { adjacencyListCache = new TIntArrayList[vertices.size()]; for (int v = 0; v < vertices.size(); v++) { Vertex<V> vertex = vertices.get(v); if (vertex != null) { adjacencyListCache[v] = new TIntArrayList(NEIGHBORS_CAPACITY); } } for (int e = 0; e < edges.size(); e++) { Edge<E> edge = edges.get(e); if (edge != null) { int v1 = edge.getV1(); int v2 = edge.getV2(); adjacencyListCache[v1].add(e); adjacencyListCache[v2].add(e); } } } return adjacencyListCache; } finally { adjacencyListCacheLock.unlock(); } } private void invalidateAdjacencyList() { adjacencyListCache = null; } @Override public void traverse(int v, Traverser<E> traverser, boolean[] encountered) { checkVertex(v); TIntArrayList[] adjacencyList = getAdjacencyList(); TIntArrayList adjacentEdges = adjacencyList[v]; encountered[v] = true; for (int i = 0; i < adjacentEdges.size(); i++) { int e = adjacentEdges.getQuick(i); Edge<E> edge = edges.get(e); int v1 = edge.getV1(); int v2 = edge.getV2(); if (!encountered[v1]) { if (traverser.traverse(v2, e, v1) == TraverseResult.CONTINUE) { encountered[v1] = true; traverse(v1, traverser, encountered); } } else if (!encountered[v2]) { if (traverser.traverse(v1, e, v2) == TraverseResult.CONTINUE) { encountered[v2] = true; traverse(v2, traverser, encountered); } } } } @Override public void traverse(int v, Traverser<E> traverser) { boolean[] encountered = new boolean[vertices.size()]; Arrays.fill(encountered, false); traverse(v, traverser, encountered); } @Override public void addListener(UndirectedGraphListener l) { listeners.add(l); } @Override public void removeListener(UndirectedGraphListener l) { listeners.remove(l); } private void notifyListener() { for (UndirectedGraphListener l : listeners) { l.graphChanged(); } } @Override public void print(PrintStream out, Function<V, String> vertexToString, Function<E, String> edgeToString) { out.append("Vertices:\n"); for (int v = 0; v < vertices.size(); v++) { Vertex<V> vertex = vertices.get(v); if (vertex != null) { String str = (vertexToString == null ? Objects.toString(vertex.getObject()) : vertexToString.apply(vertex.getObject())); out.append(Integer.toString(v)).append(": ").append(str).append("\n"); } } out.append("Edges:\n"); for (int e = 0; e < edges.size(); e++) { Edge<E> edge = edges.get(e); if (edge != null) { String str = (edgeToString == null ? Objects.toString(edge.getObject()) : edgeToString.apply(edge.getObject())); out.append(Integer.toString(e)).append(": ").append(Integer.toString(edge.getV1())).append("<->") .append(Integer.toString(edge.getV2())).append(" ").append(str).append("\n"); } } } }