How to get the result of a recursive method

I iterate over the tree structure to collect leaf node paths. Which way do you prefer to collect the result of the operation:

a) combine the results of the children and return this value

private Collection<String> extractPaths(final Element element, final IPath parentPath) { final IPath path = parentPath.append(element.getLabel()); final Collection<Element> children = getElementChildren(element); if (children.isEmpty()) return Collections.singletonList(path.toString()); final Set<String> result = new TreeSet<String>(); for (final Element child : children) result.addAll(extractPaths(child, path)); return result; } 

b) provide a result set as a parameter and add new elements at each recursion stage

 private void extractPaths(final Element element, final IPath parentPath, final Set<String> result) { final IPath path = parentPath.append(element.getLabel()); final Collection<Element> children = getElementChildren(element); if (children.isEmpty()) result.add(path.toString()); for (final Element child : children) extractPaths(child, path, result); } 
+4
source share
10 answers

The final solution that I found after some refactoring is to implement option b), but pass in the visitor instead of a collection of results:

 private void traverse(final Element element, final Visitor... visitors) { for (final Visitor visitor : visitors) // push eg the parent path to the stack visitor.push(visitor.visit(element)); for (final Element child: getElementChildren(element)) traverse(child, visitors); for (final Visitor visitor : visitors) visitor.pop(); } 

The visitor also provides a stack for transferring parent path information. This solution allows me to separate the traversal logic from the collection logic without requiring a more complex implementation of TreeIterator.

 private class CollectPathsVisitor extends ElementVisitor { public final Set<String> paths = new TreeSet<String>(); public Object visit(Element element) { final IPath parentPath = (IPath) peek(); final IPath path = parentPath.append(element.getLabel()); if (!hasChildren(element)) paths.add(path); return path; } } 
+1
source

I assume the latter is intended to call extractPaths(child, path, result) ?

The latter form will be more efficient, since it does not need to copy elements at each recursion level. According to Boris, it is less functionally clean, but Java does not really provide immutable collections with the appropriate methods for efficiently creating new collections.

From the point of view of a pleasant call, you can provide a shell in the style of the first option, which simply creates a new set and calls the second option. I would probably do this:

 private Collection<String> extractPaths(Element element, IPath parentPath) { Set<String> ret = new HashSet<String>(); extractPaths(element, parentPath, ret); return ret; } 

Another alternative is to change the third parameter from Set<String> to some kind of "collection" interface: you say that you found the result without specifying what to do with it. Indeed, the collector can return the new collector for use from now on - leaving it until implementation to decide whether to make a functionally clean version of “create a new set” or hide side effects in the collector, which will simply return again for reuse.

+6
source

Both can be used without any problems. Although the previous solution is cleaner because it does not change the input parameters. No side effects have the nature of functional programming.

+5
source

To provide the most convenient and flexible interface for your clients, write it as a class that implements Iterator<E> .

This means that the client can scroll through the elements found during the recursion, but they do not need to implement their "for each" code as a callback (Java does not have a suitable way to do this) and they can even "suspend" the operation and continue it later , outside the area in which they started it (or leave it anywhere).

This is harder to implement. If the data structure you are viewing is a tree structure with parent pointers in each node, then you do not need any data other than the current node. To go to the next node, find the first child. If there is one, then the next node. Otherwise, try the next brother. If he is not there, get the parent and try to get its next brother, etc., Until you hit zero, in this case there are no more elements.

As a quick and dirty example, here is a treenode-like class that violates all encapsulation rules in order to save some space here:

 class SimpleNode { String name; public SimpleNode parent, firstChild, nextSibling; public SimpleNode(String n) { name = n; } public void add(SimpleNode c) { c.parent = this; c.nextSibling = firstChild; firstChild = c; } public String getIndent() { StringBuffer i = new StringBuffer(); for (SimpleNode n = this; n != null; n = n.parent) i.append(" "); return i.toString(); } } 

Now create a tree from it:

 SimpleNode root = new SimpleNode("root"); SimpleNode fruit = new SimpleNode("fruit"); root.add(fruit); fruit.add(new SimpleNode("pear")); fruit.add(new SimpleNode("banana")); fruit.add(new SimpleNode("apple")); SimpleNode companies = new SimpleNode("companies"); root.add(companies); companies.add(new SimpleNode("apple")); companies.add(new SimpleNode("sun")); companies.add(new SimpleNode("microsoft")); SimpleNode colours = new SimpleNode("colours"); root.add(colours); colours.add(new SimpleNode("orange")); colours.add(new SimpleNode("red")); colours.add(new SimpleNode("blue")); 

Now, to describe this for anyone who is familiar with this idea, we want this to be done:

 for (final SimpleNode n : new SimpleNodeIterator(root)) System.out.println(n.getIndent() + "- " + n.name); 

And get this (I did the above code generated something similar to a hierarchical bullet list in SO):

  • root
    • colors
      • blue
      • red
      • Orange
    • the company
      • Microsoft
      • Sun
      • an Apple
    • fruits
      • an Apple
      • banana
      • pear

To do this, we need to map some standard operations to our SimpleNode class:

 class SimpleNodeIterator extends TreeIterator<SimpleNode> { public SimpleNodeIterator(SimpleNode root) { super(root); } protected SimpleNode getFirstChild(SimpleNode of) { return of.firstChild; } protected SimpleNode getNextSibling(SimpleNode of) { return of.nextSibling; } protected SimpleNode getParent(SimpleNode of) { return of.parent; } } 

And finally, at the bottom of our design, TreeIterator<TNode> is a reusable abstract base class that does the rest, now we told him how to navigate our node class:

 abstract class TreeIterator<TNode> implements Iterator<TNode>, Iterable<TNode> { private TNode _next; protected TreeIterator(TNode root) { _next = root; } public Iterator<TNode> iterator() { return this; } public void remove() { throw new UnsupportedOperationException(); } public boolean hasNext() { return (_next != null); } public TNode next() { if (_next == null) throw new NoSuchElementException(); TNode current = _next; _next = getFirstChild(current); for (TNode ancestor = current; (ancestor != null) && (_next == null); ancestor = getParent(ancestor)) { _next = getNextSibling(ancestor); } return current; } protected abstract TNode getFirstChild(TNode of); protected abstract TNode getNextSibling(TNode of); protected abstract TNode getParent(TNode of); } 

(Mildly naughty in that it implements Iterator<E> and Iterable<E> on the same object. It just means that you need to update a new object to repeat a second time, do not try to reuse the same object).

This means that if your hierarchical structure consists of nodes for which you can define these three simple navigation operations, then all you need to do is get your own equivalent to SimpleNodeIterator . This makes it easy to enable this feature for any tree implementation.

If what you are repeating does not have a way to get the parent, you need to save the stack during iteration. Each time you drop to a level, you push the state of the current level onto the stack. When you finish the iteration at the current level, you pop the last state from the stack and continue it. When the stack is empty, everything is ready. This means that you have an intermediate storage, but its maximum size is proportional to the depth of the recursion, not the number of elements, so assuming the data is roughly balanced, it should be much more economical than copying all the elements to the list before you will return.

+3
source

I usually prefer to return the result as I think

 $result = extractPaths($arg,$arg2); 

more clearly than

 extractPaths($arg,$arg2,$result); 

but it is completely based on taste.

+1
source

I would choose option b, since it would create fewer objects and thereby be more efficient. Solution a is similar to how you do it in a functional language, but relies on assumptions that are not fulfilled in Java.

+1
source

If you pass in the object that will be created, if you have an exception that you got to the place where you have a link to this object, then at least you will have the data that you created until no exception will be thrown.

I personally pass Builders as arguments when several methods will be built on it, including recursion. Thus, you have only one object to be built, and skip the many copies of Set, Map, or List.

+1
source

in this particular case, I prefer the latter solution, as:

  • he avoids throwing collections.
  • Your algorithm, implemented in this way, cannot get any gain from the "functional"

imho there is no real benefit of being functional without a really good reason f (e.g. using threads).

+1
source

pass the collection as a parameter to this method

+1
source

Later, fewer objects will be created in memory (as already mentioned), but it also controls each tree path only once: when retrieving and saving as a result of the installation, it is not added “added ALL” to any other set again and again and again.

0
source

All Articles