001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.graph.implementations; 033 034import java.util.Arrays; 035import java.util.HashMap; 036import java.util.Iterator; 037import java.util.NoSuchElementException; 038 039import org.graphstream.graph.Edge; 040import org.graphstream.graph.EdgeFactory; 041import org.graphstream.graph.Graph; 042import org.graphstream.graph.Node; 043import org.graphstream.graph.NodeFactory; 044 045/** 046 * <p> 047 * A lightweight graph class intended to allow the construction of big graphs 048 * (millions of elements). 049 * </p> 050 * 051 * <p> 052 * The main purpose here is to minimize memory consumption even if the 053 * management of such a graph implies more CPU consuming. See the 054 * <code>complexity</code> tags on each method so as to figure out the impact on 055 * the CPU. 056 * </p> 057 */ 058public class AdjacencyListGraph extends AbstractGraph { 059 060 public static final double GROW_FACTOR = 1.1; 061 public static final int DEFAULT_NODE_CAPACITY = 128; 062 public static final int DEFAULT_EDGE_CAPACITY = 1024; 063 064 protected HashMap<String, AbstractNode> nodeMap; 065 protected HashMap<String, AbstractEdge> edgeMap; 066 067 protected AbstractNode[] nodeArray; 068 protected AbstractEdge[] edgeArray; 069 070 protected int nodeCount; 071 protected int edgeCount; 072 073 // *** Constructors *** 074 075 /** 076 * Creates an empty graph. 077 * 078 * @param id 079 * Unique identifier of the graph. 080 * @param strictChecking 081 * If true any non-fatal error throws an exception. 082 * @param autoCreate 083 * If true (and strict checking is false), nodes are 084 * automatically created when referenced when creating a edge, 085 * even if not yet inserted in the graph. 086 * @param initialNodeCapacity 087 * Initial capacity of the node storage data structures. Use this 088 * if you know the approximate maximum number of nodes of the 089 * graph. The graph can grow beyond this limit, but storage 090 * reallocation is expensive operation. 091 * @param initialEdgeCapacity 092 * Initial capacity of the edge storage data structures. Use this 093 * if you know the approximate maximum number of edges of the 094 * graph. The graph can grow beyond this limit, but storage 095 * reallocation is expensive operation. 096 */ 097 public AdjacencyListGraph(String id, boolean strictChecking, boolean autoCreate, 098 int initialNodeCapacity, int initialEdgeCapacity) { 099 super(id, strictChecking, autoCreate); 100 101 setNodeFactory(new NodeFactory<AdjacencyListNode>() { 102 public AdjacencyListNode newInstance(String id, Graph graph) { 103 return new AdjacencyListNode((AbstractGraph) graph, id); 104 } 105 }); 106 107 setEdgeFactory(new EdgeFactory<AbstractEdge>() { 108 public AbstractEdge newInstance(String id, Node src, Node dst, 109 boolean directed) { 110 return new AbstractEdge(id, (AbstractNode) src, 111 (AbstractNode) dst, directed); 112 } 113 }); 114 115 if (initialNodeCapacity < DEFAULT_NODE_CAPACITY) 116 initialNodeCapacity = DEFAULT_NODE_CAPACITY; 117 if (initialEdgeCapacity < DEFAULT_EDGE_CAPACITY) 118 initialEdgeCapacity = DEFAULT_EDGE_CAPACITY; 119 120 nodeMap = new HashMap<String, AbstractNode>( 121 4 * initialNodeCapacity / 3 + 1); 122 edgeMap = new HashMap<String, AbstractEdge>( 123 4 * initialEdgeCapacity / 3 + 1); 124 nodeArray = new AbstractNode[initialNodeCapacity]; 125 edgeArray = new AbstractEdge[initialEdgeCapacity]; 126 nodeCount = edgeCount = 0; 127 } 128 129 /** 130 * Creates an empty graph with default edge and node capacity. 131 * 132 * @param id 133 * Unique identifier of the graph. 134 * @param strictChecking 135 * If true any non-fatal error throws an exception. 136 * @param autoCreate 137 * If true (and strict checking is false), nodes are 138 * automatically created when referenced when creating a edge, 139 * even if not yet inserted in the graph. 140 */ 141 public AdjacencyListGraph(String id, boolean strictChecking, boolean autoCreate) { 142 this(id, strictChecking, autoCreate, DEFAULT_NODE_CAPACITY, 143 DEFAULT_EDGE_CAPACITY); 144 } 145 146 /** 147 * Creates an empty graph with strict checking and without auto-creation. 148 * 149 * @param id 150 * Unique identifier of the graph. 151 */ 152 public AdjacencyListGraph(String id) { 153 this(id, true, false); 154 } 155 156 // *** Callbacks *** 157 158 @Override 159 protected void addEdgeCallback(AbstractEdge edge) { 160 edgeMap.put(edge.getId(), edge); 161 if (edgeCount == edgeArray.length) { 162 AbstractEdge[] tmp = new AbstractEdge[(int) (edgeArray.length * GROW_FACTOR) + 1]; 163 System.arraycopy(edgeArray, 0, tmp, 0, edgeArray.length); 164 Arrays.fill(edgeArray, null); 165 edgeArray = tmp; 166 } 167 edgeArray[edgeCount] = edge; 168 edge.setIndex(edgeCount++); 169 } 170 171 @Override 172 protected void addNodeCallback(AbstractNode node) { 173 nodeMap.put(node.getId(), node); 174 if (nodeCount == nodeArray.length) { 175 AbstractNode[] tmp = new AbstractNode[(int) (nodeArray.length * GROW_FACTOR) + 1]; 176 System.arraycopy(nodeArray, 0, tmp, 0, nodeArray.length); 177 Arrays.fill(nodeArray, null); 178 nodeArray = tmp; 179 } 180 nodeArray[nodeCount] = node; 181 node.setIndex(nodeCount++); 182 } 183 184 @Override 185 protected void removeEdgeCallback(AbstractEdge edge) { 186 edgeMap.remove(edge.getId()); 187 int i = edge.getIndex(); 188 edgeArray[i] = edgeArray[--edgeCount]; 189 edgeArray[i].setIndex(i); 190 edgeArray[edgeCount] = null; 191 } 192 193 @Override 194 protected void removeNodeCallback(AbstractNode node) { 195 nodeMap.remove(node.getId()); 196 int i = node.getIndex(); 197 nodeArray[i] = nodeArray[--nodeCount]; 198 nodeArray[i].setIndex(i); 199 nodeArray[nodeCount] = null; 200 } 201 202 @Override 203 protected void clearCallback() { 204 nodeMap.clear(); 205 edgeMap.clear(); 206 Arrays.fill(nodeArray, 0, nodeCount, null); 207 Arrays.fill(edgeArray, 0, edgeCount, null); 208 nodeCount = edgeCount = 0; 209 } 210 211 @SuppressWarnings("unchecked") 212 @Override 213 public <T extends Edge> T getEdge(String id) { 214 return (T) edgeMap.get(id); 215 } 216 217 @SuppressWarnings("unchecked") 218 @Override 219 public <T extends Edge> T getEdge(int index) { 220 if (index < 0 || index >= edgeCount) 221 throw new IndexOutOfBoundsException("Edge " + index 222 + " does not exist"); 223 return (T) edgeArray[index]; 224 } 225 226 @Override 227 public int getEdgeCount() { 228 return edgeCount; 229 } 230 231 @SuppressWarnings("unchecked") 232 @Override 233 public <T extends Node> T getNode(String id) { 234 return (T) nodeMap.get(id); 235 } 236 237 @SuppressWarnings("unchecked") 238 @Override 239 public <T extends Node> T getNode(int index) { 240 if (index < 0 || index > nodeCount) 241 throw new IndexOutOfBoundsException("Node " + index 242 + " does not exist"); 243 return (T) nodeArray[index]; 244 } 245 246 @Override 247 public int getNodeCount() { 248 return nodeCount; 249 } 250 251 // *** Iterators *** 252 253 protected class EdgeIterator<T extends Edge> implements Iterator<T> { 254 int iNext = 0; 255 int iPrev = -1; 256 257 public boolean hasNext() { 258 return iNext < edgeCount; 259 } 260 261 @SuppressWarnings("unchecked") 262 public T next() { 263 if (iNext >= edgeCount) 264 throw new NoSuchElementException(); 265 iPrev = iNext++; 266 return (T) edgeArray[iPrev]; 267 } 268 269 public void remove() { 270 if (iPrev == -1) 271 throw new IllegalStateException(); 272 removeEdge(edgeArray[iPrev], true, true, true); 273 iNext = iPrev; 274 iPrev = -1; 275 } 276 } 277 278 protected class NodeIterator<T extends Node> implements Iterator<T> { 279 int iNext = 0; 280 int iPrev = -1; 281 282 public boolean hasNext() { 283 return iNext < nodeCount; 284 } 285 286 @SuppressWarnings("unchecked") 287 public T next() { 288 if (iNext >= nodeCount) 289 throw new NoSuchElementException(); 290 iPrev = iNext++; 291 return (T) nodeArray[iPrev]; 292 } 293 294 public void remove() { 295 if (iPrev == -1) 296 throw new IllegalStateException(); 297 removeNode(nodeArray[iPrev], true); 298 iNext = iPrev; 299 iPrev = -1; 300 } 301 } 302 303 @Override 304 public <T extends Edge> Iterator<T> getEdgeIterator() { 305 return new EdgeIterator<T>(); 306 } 307 308 @Override 309 public <T extends Node> Iterator<T> getNodeIterator() { 310 return new NodeIterator<T>(); 311 } 312 313 /* 314 * For performance tuning 315 * 316 * @return the number of allocated but unused array elements public int 317 * getUnusedArrayElements() { int count = 0; count += edgeArray.length - 318 * edgeCount; count += nodeArray.length - nodeCount; for (ALNode n : 319 * this.<ALNode> getEachNode()) count += n.edges.length - n.degree; return 320 * count; } 321 */ 322}