Use the Composite Pattern to provide a common interface for all your objects.
An interface or abstract superclass, the Component, represents all objects that may have a relationship. One subclass, the Leaf, doesn't have any children. (There could be other non-composite Leaf-like subclasses.) Another subclass, the Composite, contains many objects, generally of any kind of Component. That allows the Composite to contain other Composites, or other non-composites such as Leaf.
The Component superclass I'm creating here is ComponentObject
. Here, all ComponentObjects
have a parent, the CategoryObject
, which is introduced below.
ComponentObject
public abstract class ComponentObject
{
private CategoryObject myParent;
protected ComponentObject(CategoryObject parent)
{
myParent = parent;
}
public CategoryObject getParent()
{
return myParent;
}
public abstract int getNumChildren();
}
It has two concrete subclasses, NonCategoryObject
, the Leaf which cannot contain children, and CategoryObject
, the Composite which encapsulates a list of children objects, which can be either other CategoryObject
s or NonCategoryObject
s.
NonCategoryObject
public class NonCategoryObject extends ComponentObject
{
public NonCategoryObject(CategoryObject parent)
{
super(parent);
}
@Override
public int getNumChildren()
{
return 0;
}
}
CategoryObject
public class CategoryObject extends ComponentObject
{
private ArrayList<ComponentObject> myChildren;
public CategoryObject(CategoryObject parent)
{
super(parent);
myChildren = new ArrayList<ComponentObject>();
}
public void addChild(ComponentObject child)
{
myChildren.add(child);
}
public ComponentObject getChild(int index)
{
return myChildren.get(index);
}
public int getNumDirectChildren()
{
return myChildren.size();
}
@Override
public int getNumChildren()
{
int numChildren = 0;
for (ComponentObject child : myChildren)
{
// One for the child itself, plus add any of the child's children.
numChildren += 1 + child.getNumChildren();
}
return numChildren;
}
}
The "getNumChildren" method is important for finding the number of children via the Composite Pattern.
Setting Up the Data
You have an ArrayList
of components, each of which knows their parent, but not who their children are:
public static void main(String[] args)
{
// Create a tree structure, parent information only.
CategoryObject root = new CategoryObject(null);
CategoryObject categoryOne = new CategoryObject(root);
CategoryObject categoryTwo = new CategoryObject(root);
CategoryObject categoryThree = new CategoryObject(root);
CategoryObject categoryOneOne = new CategoryObject(categoryOne);
CategoryObject categoryOneTwo = new CategoryObject(categoryOne);
CategoryObject categoryOneThree = new CategoryObject(categoryOne);
NonCategoryObject leafOneFour = new NonCategoryObject(categoryOne);
NonCategoryObject leafOneOneOne = new NonCategoryObject(categoryOneOne);
NonCategoryObject leafOneOneTwo = new NonCategoryObject(categoryOneTwo);
NonCategoryObject leafOneTwoOne = new NonCategoryObject(categoryOneTwo);
NonCategoryObject leafOneThreeOne = new NonCategoryObject(categoryOneThree);
NonCategoryObject leafTwoOne = new NonCategoryObject(categoryTwo);
NonCategoryObject leafThreeOne = new NonCategoryObject(categoryThree);
// We're given the ArrayList
ArrayList<ComponentObject> components = new ArrayList<ComponentObject>();
// The order doesn't matter.
components.addAll(Arrays.asList(leafOneFour, leafOneOneTwo, leafOneThreeOne,
categoryOne, categoryOneOne, categoryOneThree, root, leafThreeOne,
leafOneOneOne, categoryThree, leafOneTwoOne, leafTwoOne,
categoryTwo, categoryOneTwo));
Determine child information by looping over the list. We'll also find the root (assuming there's only one parent-less component).
ComponentObject foundRoot = null;
for (ComponentObject c : components)
{
CategoryObject parent = c.getParent();
if (parent == null)
{
foundRoot = c;
}
else
{
parent.addChild(c);
}
}
Now all parents know who their children are. Next, we'll call 2 different methods, each with their own way of determining the number of children:
// 2 methods to determine the number of children.
compositeMethod(foundRoot);
recursiveMethod(foundRoot);
}
The Methods
Here are the methods. First, the "composite" method, which takes advantage of the composite pattern to determine the number of children. It simply calls the root's getNumChildren()
method.
private static void compositeMethod(ComponentObject root)
{
int numChildren = root.getNumChildren();
System.out.println("Composite method: " + numChildren);
}
Output:
Composite method: 13
The composite method isn't quite recursive, because even though it calls itself, the object on which it calls itself is different. It calls itself on the object's children.
The recursive method, which you are interested in, calls itself at each level in the hierarchy:
private static void recursiveMethod(ComponentObject root)
{
int numChildren = findNumChildren(root);
System.out.println("Recursive method: " + numChildren);
}
// The actual recursive method.
private static int findNumChildren(ComponentObject root)
{
if (root instanceof CategoryObject)
{
CategoryObject parent = (CategoryObject) root;
int numChildren = 0;
for (int i = 0; i < parent.getNumDirectChildren(); i++)
{
ComponentObject child = parent.getChild(i);
// One for the child itself, plus its children.
numChildren += 1 + findNumChildren(child);
}
return numChildren;
}
// Base case: NonCategoryObjects have no children.
return 0;
}
Output:
Recursive method: 13