Generic (n-ary) Tree in Java
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:
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;
}
}



So what was the problem at work that necessitated this solution?
@SteveO
I was trying to represent data that had parent-child relationships an arbitrary number of levels deep.
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 ) + “]”;
@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.
Are you releasing this code as a project somewhere? It’d be nice to be able to simply include it as a library.
@Guus
Good point – I’ll try to jar it up sometime and release it as a library!
@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.
@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!