I have recently faced this question in a practical test for a job .
Suppose you are given a flat data structure like this :
**Category** **Name**
To be efficient, I'd create something like this:
public class Node
{
// My name
public String name;
// My first child. Null if no children.
public Node child;
// The next child after me under the same parent.
public Node sibling;
// The top node in the tree.
public static Node adam;
// Add first node to tree
public Node(String name)
{
this.name=name;
this.child=null;
this.sibling=null;
adam=this;
}
// Add a non-Adam node to the tree.
public Node(String name, Node parent)
{
// Copy my name. Easy part.
this.name=name;
// Make me the first child of my parent. The previous first child becomes
// my sibling. If the previous first child was null, fine, now my sibling is null.
// Note this means that children always add to the front. If this is undesirable,
// we could make this section a little more complicated to impose the desired
// order.
this.sibling=parent.child;
parent.child=this;
// As a new node, I have no children.
this.child=null;
}
// Print the current node and all nodes below it.
void printFamily(int level)
{
// You might want a fancier print function than this, like indenting or
// whatever, but I'm just trying to illustrate the principle.
System.out.println(level+" "+name);
// First process children, then process siblings.
if (child!=null)
child.printFamiliy(level+1);
if (sibling!=null)
sibling.printFamily(level);
}
// Print all nodes starting with adam
static void printFamily()
{
adam.printFamily(1);
}
// Find a node with a given name. Must traverse the tree.
public static Node findByName(String name)
{
return adam.findByName(name);
}
private Node findByNameFromHere(String name)
{
if (this.name.equals(name))
return this;
if (child!=null)
{
Node found=child.findByNameFromHere(name);
if (found!=null)
return found;
}
if (sibling!=null)
{
Node found=sibling.findByNameFromHere(name);
if (found!=null)
return found;
}
return null;
}
// Now we can add by name
public Node(String name, String parentName)
{
super(name, findByName(parentName));
}
}
Usual disclaimer: This code is off the top of my head, completely untested.
If I was doing this for a real application, I would include error checking and no doubt all sorts of peripheral stuff.
You could have a design inspired by Swing TreeModel.
EDIT When I say that, I mean you could use a class implementing a likewise interface; Noticeyou can even go as far as using directly this interface, as Swing is a part of standard JRE and is available everywhere standard Java is avaiable.
Furthermore, as it is an interface (and not a class), it's only a way for you to structure your calls. As a consequence, you can easily use it in a console based application.
Using your ArrayList as input a recursive method will be needed to print all nodes in a hierarchical/tree representation.
If you are not using recursion then this might be the reason that you cannot go to levels greater than the second one, that you mention.
Some links on recursion:
http://en.wikipedia.org/wiki/Recursion
http://www.java-samples.com/showtutorial.php?tutorialid=151
Here is some sample code that lists them in a hierarchy using recursion. The Item class has a List of children. The trick is adding any new children to the right parent. Here is the method I created to do this:
public Item getItemWithParent(int parentID){
Item result = null;
if(this.categoryID == parentID){
result = this;
} else {
for(Item nextChild : children){
result = nextChild.getItemWithParent(parentID);
if(result != null){
break;
}
}
}
return result;
}
There is probably a more efficient way, but this works.
Then, when you want to add new items to your hierarchy, do something like this:
public void addItem(int categoryID, String name, int parentID) {
Item parentItem = findParent(parentID);
parentItem.addChild(new Item(categoryID, name, parentID));
}
private Item findParent(int parentID) {
return rootNode.getItemWithParent(parentID);
}
For the actual display, I just pass in a "tab level" that says how far to tab in, then increment it for each child like this:
public String toStringHierarchy(int tabLevel){
StringBuilder builder = new StringBuilder();
for(int i = 0; i < tabLevel; i++){
builder.append("\t");
}
builder.append("-" + name);
builder.append("\n");
for(Item nextChild : children){
builder.append(nextChild.toStringHierarchy(tabLevel + 1));
}
return builder.toString();
}
Which gives me this:
-electronics
-Television
-21inch
-Test
-23inch
-LCD display
-player
-mp3player
-vcd player
-hd quality
-dvd player
public class FileReader {
Map<Integer, Employee> employees;
Employee topEmployee;
class Employee {
int id;
int mgrId;
String empName;
List<Employee> subordinates;
public Employee(String id, String mgrid, String empName) {
try {
int empId = Integer.parseInt(id);
int mgrId = 0;
if (!"Null".equals(mgrid)) {
mgrId = Integer.parseInt(mgrid);
}
this.id = empId;
this.mgrId = mgrId;
this.empName = empName;
} catch (Exception e) {
System.out.println("Unable to create Employee as the data is " + id + " " + mgrid + " " + empName);
}
}
List<Employee> getSubordinates() {
return subordinates;
}
void setSubordinates(List<Employee> subordinates) {
this.subordinates = subordinates;
}
int getId() {
return id;
}
void setId(int id) {
this.id = id;
}
int getMgrId() {
return mgrId;
}
}
public static void main(String[] args) throws IOException {
FileReader thisClass = new FileReader();
thisClass.process();
}
private void process() throws IOException {
employees = new HashMap<Integer, Employee>();
readDataAndCreateEmployees();
buildHierarchy(topEmployee);
printSubOrdinates(topEmployee, tabLevel);
}
private void readDataAndCreateEmployees() throws IOException {
BufferedReader reader = new BufferedReader(new java.io.FileReader("src/main/java/com/springapp/mvc/input.txt"));
String line = reader.readLine();
while (line != null) {
Employee employee = createEmployee(line);
employees.put(employee.getId(), employee);
if (employee.getMgrId() == 0) {
topEmployee = employee;
}
line = reader.readLine();
}
}
int tabLevel = 0;
private void printSubOrdinates(Employee topEmployee, int tabLevel) {
for (int i = 0; i < tabLevel; i++) {
System.out.print("\t");
}
System.out.println("-" + topEmployee.empName);
List<Employee> subordinates = topEmployee.getSubordinates();
System.out.print(" ");
for (Employee e : subordinates) {
printSubOrdinates(e, tabLevel+1);
}
}
public List<Employee> findAllEmployeesByMgrId(int mgrid) {
List<Employee> sameMgrEmployees = new ArrayList<Employee>();
for (Employee e : employees.values()) {
if (e.getMgrId() == mgrid) {
sameMgrEmployees.add(e);
}
}
return sameMgrEmployees;
}
private void buildHierarchy(Employee topEmployee) {
Employee employee = topEmployee;
List<Employee> employees1 = findAllEmployeesByMgrId(employee.getId());
employee.setSubordinates(employees1);
if (employees1.size() == 0) {
return;
}
for (Employee e : employees1) {
buildHierarchy(e);
}
}
private Employee createEmployee(String line) {
String[] values = line.split(" ");
Employee employee = null;
try {
if (values.length > 1) {
employee = new Employee(values[0], values[2], values[1]);
}
} catch (Exception e) {
System.out.println("Unable to create Employee as the data is " + values);
}
return employee;
}
}