When in need of a tree I typically use following interface, and implement it accordingly.
/**
* Generic node interface
*
* @param <T> type of contained data
* @param <N> self-referential type boundary that captures the implementing type
*/
interface Node<T, N extends Node<T, N>>
{
public T getObject();
public boolean addChild(N node);
public List<N> getChildren();
}
An implementation could be
class StringNode implements Node<String, StringNode>
{
private final String value;
public StringNode(String value)
{
this.value = value;
}
@Override
public String getObject()
{
return value;
}
@Override
public boolean addChild(StringNode node)
{
// add child
return false;
}
@Override
public List<StringNode> getChildren()
{
// return children
return Collections.emptyList();
}
}
The advantage here is the flexibility gained by implementing algorithms against the interface. A rather simple example could be
public <T, N extends Node<T, ? extends N>> N performAlgorithm(N node)
{
if (!node.getChildren().isEmpty())
return node.getChildren().get(0);
return node;
}
The method can be used with the inteface type or concrete implementations
StringNode sn = new StringNode("0");
Node<String, StringNode> node = sn;
// perform computations on the interface type
Node<String, StringNode> result = performAlgorithm(node);
// or use a concrete implementation
StringNode result2 = performAlgorithm(sn);