Coverage Summary for Class: Traverser (com.google.common.graph)
| Class | Method, % | Line, % |
|---|---|---|
| Traverser | 0% (0/11) | 0% (0/23) |
| Traverser$1 | 0% (0/2) | 0% (0/2) |
| Traverser$2 | 0% (0/2) | 0% (0/2) |
| Traverser$3 | 0% (0/2) | 0% (0/2) |
| Traverser$4 | 0% (0/2) | 0% (0/2) |
| Traverser$5 | 0% (0/2) | 0% (0/2) |
| Traverser$InsertionOrder | 0% (0/1) | 0% (0/3) |
| Traverser$InsertionOrder$1 | 0% (0/2) | 0% (0/2) |
| Traverser$InsertionOrder$2 | 0% (0/2) | 0% (0/2) |
| Traverser$Traversal | 0% (0/7) | 0% (0/14) |
| Traverser$Traversal$1 | 0% (0/2) | 0% (0/10) |
| Traverser$Traversal$2 | 0% (0/2) | 0% (0/6) |
| Traverser$Traversal$3 | 0% (0/2) | 0% (0/9) |
| Traverser$Traversal$4 | 0% (0/2) | 0% (0/8) |
| Total | 0% (0/41) | 0% (0/87) |
1 /* 2 * Copyright (C) 2017 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.graph; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static java.util.Objects.requireNonNull; 22 23 import com.google.common.annotations.Beta; 24 import com.google.common.collect.AbstractIterator; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.errorprone.annotations.DoNotMock; 27 import java.util.ArrayDeque; 28 import java.util.Deque; 29 import java.util.HashSet; 30 import java.util.Iterator; 31 import java.util.Set; 32 import javax.annotation.CheckForNull; 33 34 /** 35 * An object that can traverse the nodes that are reachable from a specified (set of) start node(s) 36 * using a specified {@link SuccessorsFunction}. 37 * 38 * <p>There are two entry points for creating a {@code Traverser}: {@link 39 * #forTree(SuccessorsFunction)} and {@link #forGraph(SuccessorsFunction)}. You should choose one 40 * based on your answers to the following questions: 41 * 42 * <ol> 43 * <li>Is there only one path to any node that's reachable from any start node? (If so, the graph 44 * to be traversed is a tree or forest even if it is a subgraph of a graph which is neither.) 45 * <li>Are the node objects' implementations of {@code equals()}/{@code hashCode()} <a 46 * href="https://github.com/google/guava/wiki/GraphsExplained#non-recursiveness">recursive</a>? 47 * </ol> 48 * 49 * <p>If your answers are: 50 * 51 * <ul> 52 * <li>(1) "no" and (2) "no", use {@link #forGraph(SuccessorsFunction)}. 53 * <li>(1) "yes" and (2) "yes", use {@link #forTree(SuccessorsFunction)}. 54 * <li>(1) "yes" and (2) "no", you can use either, but {@code forTree()} will be more efficient. 55 * <li>(1) "no" and (2) "yes", <b><i>neither will work</i></b>, but if you transform your node 56 * objects into a non-recursive form, you can use {@code forGraph()}. 57 * </ul> 58 * 59 * @author Jens Nyman 60 * @param <N> Node parameter type 61 * @since 23.1 62 */ 63 @Beta 64 @DoNotMock( 65 "Call forGraph or forTree, passing a lambda or a Graph with the desired edges (built with" 66 + " GraphBuilder)") 67 @ElementTypesAreNonnullByDefault 68 public abstract class Traverser<N> { 69 private final SuccessorsFunction<N> successorFunction; 70 71 private Traverser(SuccessorsFunction<N> successorFunction) { 72 this.successorFunction = checkNotNull(successorFunction); 73 } 74 75 /** 76 * Creates a new traverser for the given general {@code graph}. 77 * 78 * <p>Traversers created using this method are guaranteed to visit each node reachable from the 79 * start node(s) at most once. 80 * 81 * <p>If you know that no node in {@code graph} is reachable by more than one path from the start 82 * node(s), consider using {@link #forTree(SuccessorsFunction)} instead. 83 * 84 * <p><b>Performance notes</b> 85 * 86 * <ul> 87 * <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from 88 * the start node), assuming that the node objects have <i>O(1)</i> {@code equals()} and 89 * {@code hashCode()} implementations. (See the <a 90 * href="https://github.com/google/guava/wiki/GraphsExplained#elements-must-be-useable-as-map-keys"> 91 * notes on element objects</a> for more information.) 92 * <li>While traversing, the traverser will use <i>O(n)</i> space (where <i>n</i> is the number 93 * of nodes that have thus far been visited), plus <i>O(H)</i> space (where <i>H</i> is the 94 * number of nodes that have been seen but not yet visited, that is, the "horizon"). 95 * </ul> 96 * 97 * @param graph {@link SuccessorsFunction} representing a general graph that may have cycles. 98 */ 99 public static <N> Traverser<N> forGraph(final SuccessorsFunction<N> graph) { 100 return new Traverser<N>(graph) { 101 @Override 102 Traversal<N> newTraversal() { 103 return Traversal.inGraph(graph); 104 } 105 }; 106 } 107 108 /** 109 * Creates a new traverser for a directed acyclic graph that has at most one path from the start 110 * node(s) to any node reachable from the start node(s), and has no paths from any start node to 111 * any other start node, such as a tree or forest. 112 * 113 * <p>{@code forTree()} is especially useful (versus {@code forGraph()}) in cases where the data 114 * structure being traversed is, in addition to being a tree/forest, also defined <a 115 * href="https://github.com/google/guava/wiki/GraphsExplained#non-recursiveness">recursively</a>. 116 * This is because the {@code forTree()}-based implementations don't keep track of visited nodes, 117 * and therefore don't need to call `equals()` or `hashCode()` on the node objects; this saves 118 * both time and space versus traversing the same graph using {@code forGraph()}. 119 * 120 * <p>Providing a graph to be traversed for which there is more than one path from the start 121 * node(s) to any node may lead to: 122 * 123 * <ul> 124 * <li>Traversal not terminating (if the graph has cycles) 125 * <li>Nodes being visited multiple times (if multiple paths exist from any start node to any 126 * node reachable from any start node) 127 * </ul> 128 * 129 * <p><b>Performance notes</b> 130 * 131 * <ul> 132 * <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from 133 * the start node). 134 * <li>While traversing, the traverser will use <i>O(H)</i> space (where <i>H</i> is the number 135 * of nodes that have been seen but not yet visited, that is, the "horizon"). 136 * </ul> 137 * 138 * <p><b>Examples</b> (all edges are directed facing downwards) 139 * 140 * <p>The graph below would be valid input with start nodes of {@code a, f, c}. However, if {@code 141 * b} were <i>also</i> a start node, then there would be multiple paths to reach {@code e} and 142 * {@code h}. 143 * 144 * <pre>{@code 145 * a b c 146 * / \ / \ | 147 * / \ / \ | 148 * d e f g 149 * | 150 * | 151 * h 152 * }</pre> 153 * 154 * <p>. 155 * 156 * <p>The graph below would be a valid input with start nodes of {@code a, f}. However, if {@code 157 * b} were a start node, there would be multiple paths to {@code f}. 158 * 159 * <pre>{@code 160 * a b 161 * / \ / \ 162 * / \ / \ 163 * c d e 164 * \ / 165 * \ / 166 * f 167 * }</pre> 168 * 169 * <p><b>Note on binary trees</b> 170 * 171 * <p>This method can be used to traverse over a binary tree. Given methods {@code 172 * leftChild(node)} and {@code rightChild(node)}, this method can be called as 173 * 174 * <pre>{@code 175 * Traverser.forTree(node -> ImmutableList.of(leftChild(node), rightChild(node))); 176 * }</pre> 177 * 178 * @param tree {@link SuccessorsFunction} representing a directed acyclic graph that has at most 179 * one path between any two nodes 180 */ 181 public static <N> Traverser<N> forTree(final SuccessorsFunction<N> tree) { 182 if (tree instanceof BaseGraph) { 183 checkArgument(((BaseGraph<?>) tree).isDirected(), "Undirected graphs can never be trees."); 184 } 185 if (tree instanceof Network) { 186 checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees."); 187 } 188 return new Traverser<N>(tree) { 189 @Override 190 Traversal<N> newTraversal() { 191 return Traversal.inTree(tree); 192 } 193 }; 194 } 195 196 /** 197 * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in 198 * the order of a breadth-first traversal. That is, all the nodes of depth 0 are returned, then 199 * depth 1, then 2, and so on. 200 * 201 * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in 202 * the order {@code abcdef} (assuming successors are returned in alphabetical order). 203 * 204 * <pre>{@code 205 * b ---- a ---- d 206 * | | 207 * | | 208 * e ---- c ---- f 209 * }</pre> 210 * 211 * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change 212 * while iteration is in progress. 213 * 214 * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will 215 * compute its next element on the fly. It is thus possible to limit the traversal to a certain 216 * number of nodes as follows: 217 * 218 * <pre>{@code 219 * Iterables.limit(Traverser.forGraph(graph).breadthFirst(node), maxNumberOfNodes); 220 * }</pre> 221 * 222 * <p>See <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Wikipedia</a> for more 223 * info. 224 * 225 * @throws IllegalArgumentException if {@code startNode} is not an element of the graph 226 */ 227 public final Iterable<N> breadthFirst(N startNode) { 228 return breadthFirst(ImmutableSet.of(startNode)); 229 } 230 231 /** 232 * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code 233 * startNodes}, in the order of a breadth-first traversal. This is equivalent to a breadth-first 234 * traversal of a graph with an additional root node whose successors are the listed {@code 235 * startNodes}. 236 * 237 * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph 238 * @see #breadthFirst(Object) 239 * @since 24.1 240 */ 241 public final Iterable<N> breadthFirst(Iterable<? extends N> startNodes) { 242 final ImmutableSet<N> validated = validate(startNodes); 243 return new Iterable<N>() { 244 @Override 245 public Iterator<N> iterator() { 246 return newTraversal().breadthFirst(validated.iterator()); 247 } 248 }; 249 } 250 251 /** 252 * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in 253 * the order of a depth-first pre-order traversal. "Pre-order" implies that nodes appear in the 254 * {@code Iterable} in the order in which they are first visited. 255 * 256 * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in 257 * the order {@code abecfd} (assuming successors are returned in alphabetical order). 258 * 259 * <pre>{@code 260 * b ---- a ---- d 261 * | | 262 * | | 263 * e ---- c ---- f 264 * }</pre> 265 * 266 * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change 267 * while iteration is in progress. 268 * 269 * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will 270 * compute its next element on the fly. It is thus possible to limit the traversal to a certain 271 * number of nodes as follows: 272 * 273 * <pre>{@code 274 * Iterables.limit( 275 * Traverser.forGraph(graph).depthFirstPreOrder(node), maxNumberOfNodes); 276 * }</pre> 277 * 278 * <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info. 279 * 280 * @throws IllegalArgumentException if {@code startNode} is not an element of the graph 281 */ 282 public final Iterable<N> depthFirstPreOrder(N startNode) { 283 return depthFirstPreOrder(ImmutableSet.of(startNode)); 284 } 285 286 /** 287 * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code 288 * startNodes}, in the order of a depth-first pre-order traversal. This is equivalent to a 289 * depth-first pre-order traversal of a graph with an additional root node whose successors are 290 * the listed {@code startNodes}. 291 * 292 * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph 293 * @see #depthFirstPreOrder(Object) 294 * @since 24.1 295 */ 296 public final Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes) { 297 final ImmutableSet<N> validated = validate(startNodes); 298 return new Iterable<N>() { 299 @Override 300 public Iterator<N> iterator() { 301 return newTraversal().preOrder(validated.iterator()); 302 } 303 }; 304 } 305 306 /** 307 * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in 308 * the order of a depth-first post-order traversal. "Post-order" implies that nodes appear in the 309 * {@code Iterable} in the order in which they are visited for the last time. 310 * 311 * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in 312 * the order {@code fcebda} (assuming successors are returned in alphabetical order). 313 * 314 * <pre>{@code 315 * b ---- a ---- d 316 * | | 317 * | | 318 * e ---- c ---- f 319 * }</pre> 320 * 321 * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change 322 * while iteration is in progress. 323 * 324 * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will 325 * compute its next element on the fly. It is thus possible to limit the traversal to a certain 326 * number of nodes as follows: 327 * 328 * <pre>{@code 329 * Iterables.limit( 330 * Traverser.forGraph(graph).depthFirstPostOrder(node), maxNumberOfNodes); 331 * }</pre> 332 * 333 * <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info. 334 * 335 * @throws IllegalArgumentException if {@code startNode} is not an element of the graph 336 */ 337 public final Iterable<N> depthFirstPostOrder(N startNode) { 338 return depthFirstPostOrder(ImmutableSet.of(startNode)); 339 } 340 341 /** 342 * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code 343 * startNodes}, in the order of a depth-first post-order traversal. This is equivalent to a 344 * depth-first post-order traversal of a graph with an additional root node whose successors are 345 * the listed {@code startNodes}. 346 * 347 * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph 348 * @see #depthFirstPostOrder(Object) 349 * @since 24.1 350 */ 351 public final Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes) { 352 final ImmutableSet<N> validated = validate(startNodes); 353 return new Iterable<N>() { 354 @Override 355 public Iterator<N> iterator() { 356 return newTraversal().postOrder(validated.iterator()); 357 } 358 }; 359 } 360 361 abstract Traversal<N> newTraversal(); 362 363 @SuppressWarnings("CheckReturnValue") 364 private ImmutableSet<N> validate(Iterable<? extends N> startNodes) { 365 ImmutableSet<N> copy = ImmutableSet.copyOf(startNodes); 366 for (N node : copy) { 367 successorFunction.successors(node); // Will throw if node doesn't exist 368 } 369 return copy; 370 } 371 372 /** 373 * Abstracts away the difference between traversing a graph vs. a tree. For a tree, we just take 374 * the next element from the next non-empty iterator; for graph, we need to loop through the next 375 * non-empty iterator to find first unvisited node. 376 */ 377 private abstract static class Traversal<N> { 378 final SuccessorsFunction<N> successorFunction; 379 380 Traversal(SuccessorsFunction<N> successorFunction) { 381 this.successorFunction = successorFunction; 382 } 383 384 static <N> Traversal<N> inGraph(SuccessorsFunction<N> graph) { 385 final Set<N> visited = new HashSet<>(); 386 return new Traversal<N>(graph) { 387 @Override 388 @CheckForNull 389 N visitNext(Deque<Iterator<? extends N>> horizon) { 390 Iterator<? extends N> top = horizon.getFirst(); 391 while (top.hasNext()) { 392 N element = top.next(); 393 // requireNonNull is safe because horizon contains only graph nodes. 394 /* 395 * TODO(cpovirk): Replace these two statements with one (`N element = 396 * requireNonNull(top.next())`) once our checker supports it. 397 * 398 * (The problem is likely 399 * https://github.com/jspecify/nullness-checker-for-checker-framework/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecAnnotatedTypeFactory.java#L896) 400 */ 401 requireNonNull(element); 402 if (visited.add(element)) { 403 return element; 404 } 405 } 406 horizon.removeFirst(); 407 return null; 408 } 409 }; 410 } 411 412 static <N> Traversal<N> inTree(SuccessorsFunction<N> tree) { 413 return new Traversal<N>(tree) { 414 @CheckForNull 415 @Override 416 N visitNext(Deque<Iterator<? extends N>> horizon) { 417 Iterator<? extends N> top = horizon.getFirst(); 418 if (top.hasNext()) { 419 return checkNotNull(top.next()); 420 } 421 horizon.removeFirst(); 422 return null; 423 } 424 }; 425 } 426 427 final Iterator<N> breadthFirst(Iterator<? extends N> startNodes) { 428 return topDown(startNodes, InsertionOrder.BACK); 429 } 430 431 final Iterator<N> preOrder(Iterator<? extends N> startNodes) { 432 return topDown(startNodes, InsertionOrder.FRONT); 433 } 434 435 /** 436 * In top-down traversal, an ancestor node is always traversed before any of its descendant 437 * nodes. The traversal order among descendant nodes (particularly aunts and nieces) are 438 * determined by the {@code InsertionOrder} parameter: nieces are placed at the FRONT before 439 * aunts for pre-order; while in BFS they are placed at the BACK after aunts. 440 */ 441 private Iterator<N> topDown(Iterator<? extends N> startNodes, final InsertionOrder order) { 442 final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>(); 443 horizon.add(startNodes); 444 return new AbstractIterator<N>() { 445 @Override 446 @CheckForNull 447 protected N computeNext() { 448 do { 449 N next = visitNext(horizon); 450 if (next != null) { 451 Iterator<? extends N> successors = successorFunction.successors(next).iterator(); 452 if (successors.hasNext()) { 453 // BFS: horizon.addLast(successors) 454 // Pre-order: horizon.addFirst(successors) 455 order.insertInto(horizon, successors); 456 } 457 return next; 458 } 459 } while (!horizon.isEmpty()); 460 return endOfData(); 461 } 462 }; 463 } 464 465 final Iterator<N> postOrder(Iterator<? extends N> startNodes) { 466 final Deque<N> ancestorStack = new ArrayDeque<>(); 467 final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>(); 468 horizon.add(startNodes); 469 return new AbstractIterator<N>() { 470 @Override 471 @CheckForNull 472 protected N computeNext() { 473 for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) { 474 Iterator<? extends N> successors = successorFunction.successors(next).iterator(); 475 if (!successors.hasNext()) { 476 return next; 477 } 478 horizon.addFirst(successors); 479 ancestorStack.push(next); 480 } 481 return ancestorStack.isEmpty() ? endOfData() : ancestorStack.pop(); 482 } 483 }; 484 } 485 486 /** 487 * Visits the next node from the top iterator of {@code horizon} and returns the visited node. 488 * Null is returned to indicate reaching the end of the top iterator. 489 * 490 * <p>For example, if horizon is {@code [[a, b], [c, d], [e]]}, {@code visitNext()} will return 491 * {@code [a, b, null, c, d, null, e, null]} sequentially, encoding the topological structure. 492 * (Note, however, that the callers of {@code visitNext()} often insert additional iterators 493 * into {@code horizon} between calls to {@code visitNext()}. This causes them to receive 494 * additional values interleaved with those shown above.) 495 */ 496 @CheckForNull 497 abstract N visitNext(Deque<Iterator<? extends N>> horizon); 498 } 499 500 /** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */ 501 private enum InsertionOrder { 502 FRONT { 503 @Override 504 <T> void insertInto(Deque<T> deque, T value) { 505 deque.addFirst(value); 506 } 507 }, 508 BACK { 509 @Override 510 <T> void insertInto(Deque<T> deque, T value) { 511 deque.addLast(value); 512 } 513 }; 514 515 abstract <T> void insertInto(Deque<T> deque, T value); 516 } 517 }