Home > Java, Programming and Development > Generic (n-ary) Tree in Java

Generic (n-ary) Tree in Java

January 30th, 2010 vivin Leave a comment Go to comments

Last week I was solving a problem at work that required the use of a Generic (n-ary) Tree. An n-ary tree is a tree where node can have between 0 and n children. There is a special case of n-ary trees where each node can have at most n nodes (k-ary tree). This implementation focuses on the most general case, where any node can have between 0 and n children. Java doesn’t have a Tree or Tree Node data structure. I couldn’t find any third-party implementations either (like in commons-lang). So I decided to write my own.

First, we need to define the node of our tree. Our node has two attributes. One is the data, and another is a List which can contain references to the children of that node:

Structure of a Generic Tree Node

Structure of a Generic Tree Node

The code for a Generic Tree Node looks like this:

GenericTreeNode.java

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GenericTreeNode<T> {

    public T data;
    public List<GenericTreeNode<T>> children;

    public GenericTreeNode() {
        super();
        children = new ArrayList<GenericTreeNode<T>>();
    }

    public GenericTreeNode(T data) {
        this();
        setData(data);
    }

    public List<GenericTreeNode<T>> getChildren() {
        return this.children;
    }

    public int getNumberOfChildren() {
        return getChildren().size();
    }

    public boolean hasChildren() {
        return (getNumberOfChildren() > 0);
    }

    public void setChildren(List<GenericTreeNode<T>> children) {
        this.children = children;
    }

    public void addChild(GenericTreeNode<T> child) {
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode<T> child) throws IndexOutOfBoundsException {
        children.add(index, child);
    }

    public void removeChildren() {
        this.children = new ArrayList<GenericTreeNode<T>>();
    }

    public void removeChildAt(int index) throws IndexOutOfBoundsException {
        children.remove(index);
    }

    public GenericTreeNode<T> getChildAt(int index) throws IndexOutOfBoundsException {
        return children.get(index);
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String toString() {
        return getData().toString();
    }

    public boolean equals(GenericTreeNode<T> node) {
        return node.getData().equals(getData());
    }

    public int hashCode() {
        return getData().hashCode();
    }

    public String toStringVerbose() {
        String stringRepresentation = getData().toString() + ":[";

        for (GenericTreeNode<T> node : getChildren()) {
            stringRepresentation += node.getData().toString() + ", ";
        }

        //Pattern.DOTALL causes ^ and $ to match. Otherwise it won't. It's retarded.
        Pattern pattern = Pattern.compile(", $", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(stringRepresentation);

        stringRepresentation = matcher.replaceFirst("");
        stringRepresentation += "]";

        return stringRepresentation;
    }
}

Related Posts

  1. SteveO
    January 30th, 2010 at 23:24 | #1

    So what was the problem at work that necessitated this solution?

  2. January 31st, 2010 at 10:26 | #2

    @SteveO
    I was trying to represent data that had parent-child relationships an arbitrary number of levels deep.

  3. February 1st, 2010 at 11:13 | #3

    I believe it’s a rough implementation of your idea.
    Which one do you prefer? Using Pattern, Matcher etc over -
    return (this.children.size()>0? str.substring(0, str.lenth()-2) : str ) + “]”;

  4. February 1st, 2010 at 16:10 | #4

    @Lobo
    I actually prefer the regex because it’s more obvious what I’m doing. When using a substring, it’s not immediately obvious (without comments) what you’re doing. I also gravitate towards to regexes due to the fact that I’ve written a lot of Perl code. Regexes aren’t as elegant in Java though.

  5. Guus
    February 17th, 2010 at 02:31 | #5

    Are you releasing this code as a project somewhere? It’d be nice to be able to simply include it as a library.

  6. February 17th, 2010 at 09:20 | #6

    @Guus
    Good point – I’ll try to jar it up sometime and release it as a library!

  7. Guus
    February 17th, 2010 at 09:52 | #7

    @vivin
    Actually, I couldn’t wait and made a little Maven project out of it myself. I’ll be happy to send it to you, if you’d like me to.

    There appears to be a small omission in the code: you’re not overriding Object.equals(Object obj) even though you intent to. Instead, you have GenericTreeNode.equals(GenericTreeNode obj).

    Poke me via mail (I included it in the header of this comment) and I’ll send you a project archive.

  8. February 17th, 2010 at 10:20 | #8

    @Guus
    Ah, good point. Thanks for catching that! I’ll send you an email – thanks for taking the time to make a project. I do have a google code account – I should probably move this into that!

  1. No trackbacks yet.