Source:
<code>public <U> Object traverse(Collection<? extends T> roots, TraverserVisitor<? super T> visitor) { assertNotNull(roots); assertNotNull(visitor); traverserState.addNewContexts(roots, traverserState.newContext(null, null, rootVars)); TraverserContext currentContext = null; traverseLoop: while (!traverserState.isEmpty()) { Object top = traverserState.pop(); if (top == TraverserState.Marker.END_LIST) { TraversalControl traversalControl = visitor.leave((TraverserContext) traverserState.pop()); assertNotNull(traversalControl, "result of leave must not be null"); switch (traversalControl) { case QUIT: break traverseLoop; case ABORT: case CONTINUE: continue; default: assertShouldNeverHappen(); } } currentContext = (TraverserContext) top; if (currentContext.isVisited()) { visitor.backRef(currentContext); } else { TraversalControl traversalControl = visitor.enter(currentContext); assertNotNull(traversalControl, "result of enter must not be null"); <START> this.traverserState.addVisited((T) currentContext.thisNode()); <END> switch (traversalControl) { case QUIT: break traverseLoop; case ABORT: continue; case CONTINUE: traverserState.pushAll(currentContext, getChildren); continue; default: assertShouldNeverHappen(); } } } return currentContext.getResult(); }</code><technical_language>true, a side effect in isVisited I consider ok: is extremely unexpected understanding code hard. I mind double lookup, I a real measurable performance impact</technical_language>

Target:
public TraverserResult traverse(Collection<? extends T> roots, TraverserVisitor<? super T> visitor) { assertNotNull(roots); assertNotNull(visitor); traverserState.addNewContexts(roots, traverserState.newContext(null, null, rootVars)); TraverserContext currentContext = null; traverseLoop: while (!traverserState.isEmpty()) { Object top = traverserState.pop(); if (top == TraverserState.Marker.END_LIST) { TraverserContext contextForLeave = (TraverserContext) traverserState.pop(); currentContext = contextForLeave; TraversalControl traversalControl = visitor.leave(contextForLeave); assertNotNull(traversalControl, "result of leave must not be null"); assertTrue(CONTINUE_OR_QUIT.contains(traversalControl), "result can only return CONTINUE or QUIT"); switch (traversalControl) { case QUIT: break traverseLoop; case CONTINUE: continue; default: assertShouldNeverHappen(); } } currentContext = (TraverserContext) top; if (currentContext.isVisited()) { TraversalControl traversalControl = visitor.backRef(currentContext); assertNotNull(traversalControl, "result of backRef must not be null"); assertTrue(CONTINUE_OR_QUIT.contains(traversalControl), "backRef can only return CONTINUE or QUIT"); if (traversalControl == QUIT) { break traverseLoop; } } else { TraversalControl traversalControl = visitor.enter(currentContext); assertNotNull(traversalControl, "result of enter must not be null"); this.traverserState.addVisited((T) currentContext.thisNode()); switch (traversalControl) { case QUIT: break traverseLoop; case ABORT: continue; case CONTINUE: traverserState.pushAll(currentContext, getChildren); continue; default: assertShouldNeverHappen(); } } } TraverserResult traverserResult = new TraverserResult(currentContext.getResult()); return traverserResult; }