Steve Perkins
Steve Perkins

Reputation: 11880

Java tree structure with multiple children (sorted) at each level

I'm working with a flat List of objects, which nevertheless are associated with each other in parent-child relationships. An object may have any number of children, or none at all. I need to display these objects as a tree, showing those relationships. Each level of the tree should be sorted (the objects are compatible with Collections.sort() ).

The question is two-part:

  1. Does Java have a good out-of-the-box data structure for holding such a tree, or do I need to write one from scratch? (not a huge task, but there's no sense in reinventing the wheel) I know about DefaultTreeModel in Swing... but this application is running on the server-side, and use of the Swing package will get frowned upon in code review.

  2. What would be the best pattern for loading a flat List into such a data-structure? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc. However, for the requirement of sorting the peers at each level in the tree... I'm not sure if it makes more sense to worry about this when I'm building the tree, or worry about it later when I'm parsing the tree for display.

Upvotes: 9

Views: 37232

Answers (4)

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298938

Here is a quick-and-dirty Tree implementation that uses TreeSets on all levels (you can supply a comparator, or natural ordering will be used):

public class Tree<T> {

    private final Node<T> rootElement;

    public void visitNodes(final NodeVisitor<T> visitor){
        doVisit(rootElement, visitor);
    }

    private static <T> boolean doVisit(final Node<T> node,
        final NodeVisitor<T> visitor){
        boolean result = visitor.visit(node);
        if(result){
            for(final Node<T> subNode : node.children){
                if(!doVisit(subNode, visitor)){
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

    public interface NodeVisitor<T> {

        boolean visit(Node<T> node);
    }

    public Node<T> getRootElement(){
        return rootElement;
    }

    private static final class NodeComparator<T> implements Comparator<Node<T>>{

        private final Comparator<T> wrapped;

        @Override
        public int compare(final Node<T> o1, final Node<T> o2){
            return wrapped.compare(o1.value, o2.value);
        }

        public NodeComparator(final Comparator<T> wrappedComparator){
            this.wrapped = wrappedComparator;
        }

    }

    public static class Node<T> {

        private final SortedSet<Node<T>> children;

        private final Node<T> parent;

        private T value;

        private final Comparator<?> comparator;

        @SuppressWarnings("unchecked")
        Node(final T value, final Node<T> parent, final Comparator<?> comparator){
            this.value = value;
            this.parent = parent;
            this.comparator = comparator;
            children =
                new TreeSet<Node<T>>(new NodeComparator<T>((Comparator<T>) comparator));
        }

        public List<Node<T>> getChildren(){
            return new ArrayList<Node<T>>(children);
        }

        public Node<T> getParent(){
            return parent;
        }

        public T getValue(){
            return value;
        }

        public void setValue(final T value){
            this.value = value;
        }

        public Node<T> addChild(final T value){
            final Node<T> node = new Node<T>(value, this, comparator);
            return children.add(node) ? node : null;
        }

    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NATURAL_ORDER = new Comparator(){

        @SuppressWarnings("unchecked")
        @Override
        public int compare(final Object o1, final Object o2){
            return ((Comparable) o1).compareTo(o2);
        }
    };

    private final Comparator<?> comparator;

    public Tree(){
        this(null, null);
    }

    public Tree(final Comparator<? super T> comparator){
        this(comparator, null);
    }

    public Tree(final Comparator<? super T> comparator, final T rootValue){
        this.comparator = comparator == null ? NATURAL_ORDER : comparator;
        this.rootElement = new Node<T>(rootValue, null, this.comparator);
    }

    public Tree(final T rootValue){
        this(null, rootValue);
    }

}

Here is some sample code against it:

final Tree<Integer> tree = new Tree<Integer>();
final Node<Integer> rootNode = tree.getRootElement();
rootNode.setValue(1);
final Node<Integer> childNode = rootNode.addChild(2);
final Node<Integer> newChildNode = rootNode.addChild(3);
newChildNode.addChild(4);
tree.visitNodes(new NodeVisitor<Integer>(){

    @Override
    public boolean visit(final Node<Integer> node){
        final StringBuilder sb = new StringBuilder();
        Node<Integer> curr = node;
        do{
            if(sb.length() > 0){
                sb.insert(0, " > ");
            }
            sb.insert(0, String.valueOf(curr.getValue()));
            curr = curr.getParent();
        } while(curr != null);
        System.out.println(sb);
        return true;
    }
});

Output:

1
1 > 2
1 > 3
1 > 3 > 4

Upvotes: 9

Jason Orendorff
Jason Orendorff

Reputation: 45116

The approach you came up with is what I would do.

How to go about building the tree really depends on what information you have in the initial List.

  • If each node contains a reference to its parent and a collection of its children, you don't need to build anything other than the root set.

  • If each node only has a reference to its parent, you do need to build a tree; but you can do it in a single pass over the data using a HashMap to map each node to a list (which you build) of its children.

  • If the nodes don't even contain a reference to their parents, you'll have to do what Péter suggests.

In any case, I wouldn't bother sorting the whole List first. Sorting a large List will be slower than sorting lots of little ones with the same total length. (This follows from sorting being O(n log n).)

Upvotes: 0

P&#233;ter T&#246;r&#246;k
P&#233;ter T&#246;r&#246;k

Reputation: 116266

What would be the best pattern for loading a flat List into such a data-structure? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc.

If I understand correctly, you only have a flat list, without any concrete associations between its elements, and you can detect somehow whether a particular element is the child of another.

In this case, you could

  1. sort the list
  2. (identify the root node, if it is not known yet)
  3. put the root into a queue
  4. take the first node from the queue
  5. starting from the first element of the list, check each element whether it is a child of the current node; if so, add it to the current level of the tree and put it into the queue
  6. repeat from step 4.

If detecting parent-child relationship is costly, you could improve performance by storing a flag for / nulling out each node whose location within the tree is already identified, so that you can jump over them when traversing the list. Alternatively, you may copy the whole sorted list into a linked list so that it is trivial to remove processed elements from it.

Upvotes: 4

Navi
Navi

Reputation: 8736

There are no tree structures in Java, but there are sorted ones: TreeSet and TreeMap. See for some hints java data-structure to simulate a data tree

Upvotes: 1

Related Questions