琢磨其他东西的时候弄出来的副产品,自娱自乐用。
树的节点有名称和值两个公开的属性。每一个节点可以重名、重值,靠节点的内部UUID区分。因此,这个树类内部并不排序,是一种“乱序”树,因而也是“自由”树。
TreeNode类使用了Java的泛型技术,所以树节点的value可以是任何类型(但是同一棵树的所有节点的value只能是同种类型),name属性是字符串类型。
TreeNode的完整代码如下:
package generic;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import java.util.Vector;
/**
* 实现数据结构——树的类。<br>
* 特点:
* <ul>
* <li>纵向无限层级,横向无限扩展</li>
* <li>元素不排序,不同元素的名称和值均可相同,靠UUID区分不同元素</li>
* <li>支持将新节点插入到当前节点的子节点列表的任意位置</li>
* <li>支持将一个树插入到另一棵树种,前提是两棵树的节点值具有相同的类型</li>
* <li>采用泛型编程,所以节点可以是任意类型</li>
* <li>可以用getLevel()方法获得节点在树中的层级</li>
* <li>支持深度优先和广度优先两种遍历方法</li>
* <li>适用于分类树、目录树、XML的DOM树等允许节点内容重复的情形</li>
* </ul>
*
* 限制:
* <ul>
* <li>不直接支持插入孙节点及更下层的节点,要先找到子节点,再为该子节点插入子节点</li>
* <li>由于元素不排序,所以搜索效率不高,不适用于大规模树</li>
* <li>未考虑线程安全问题,所以在多线程情况下慎用</li>
* <li>树中所有节点的值必须是同一类型</li>
* </ul>
*
* @author kingfox
* @version 1.00
* @param <T> 指定树元素的类型
*/
public class TreeNode<T>
{
/** 树节点的内部id */
protected UUID uuid;
/** 树节点的层级 */
protected int level;
/** 树节点的名字 */
public String name;
/** 树节点的值 */
public T value;
/** 树的父节点。对于树的根节点来说,该属性为null。 */
protected TreeNode<T> parent;
/** 树节点的子节点。 */
protected List<TreeNode<T>> children;
/**
* 构造函数:构造树节点,其父节点默认为null。
* @param name 指定树节点的名称
* @param value 指定树节点的值
*/
public TreeNode(String name, T value)
{
super();
initNode(name, value, null);
}
/**
* 构造函数:构造树节点,并挂接到某个父节点下。
* @param name 树节点的名称
* @param value 树节点的值
* @param parent 指定父节点
*/
public TreeNode(String name, T value, TreeNode<T> parent)
{
super();
initNode(name, value, parent);
}
protected void initNode(String name, T value, TreeNode<T> parent)
{
this.name = name;
this.value = value;
this.parent = parent;
if (parent == null)
{
level = 0;
}
else
{
level = parent.level + 1;
}
this.children = new Vector<>();
this.uuid = UUID.randomUUID();
}
/**
* 获取节点的ID。
* @return 返回节点的UUID。
*/
public UUID getUuid()
{
return uuid;
}
/**
* 获取该节点在树中的层级
* @return 返回节点在树中的层级
*/
public int getLevel()
{
return level;
}
/**
* 获取节点的父节点。
* @return 返回节点的父节点。
*/
public TreeNode<T> getParent()
{
return parent;
}
/**
* 找到树的根节点。
* @return 返回树的根节点。
*/
public TreeNode<T> getRoot()
{
TreeNode<T> root = this;
while(root.parent != null)
{
root = root.parent;
}
return root;
}
/**
* 获取树节点的兄弟节点。
* @return 返回该节点的所有兄弟节点。
*/
public List<TreeNode<T>> getNeighbours()
{
return (parent != null) ? parent.children : null;
}
/**
* 获取节点的子节点列表。
* @return 返回节点的子节点列表。
*/
public List<TreeNode<T>> getChildren()
{
return children;
}
/**
* 新增一个子节点。
* @param child 子节点对象。
*/
public void addChild(TreeNode<T> child)
{
if (findNode(child.uuid, getRoot()) == null) // 被插入节点在当前树中不存在时才会将该节点插入到树中。
{
child.parent = this;
child.level = level + 1;
children.add(child);
}
}
/**
* 将子节点插入到指定位置。
* @param index 指定新增子节点在已有子节点中的位置。
* @param child 新增的子节点对象。
*/
public void addChild(int index, TreeNode<T> child)
{
if (findNode(child.uuid, getRoot()) != null)
{
child.parent = this;
child.level = level + 1;
children.add(index, child);
}
}
/**
* 新增一个子节点
* @param name 新增子节点的名称
* @param value 新增子节点的值
* @return 返回新增的子节点
*/
public TreeNode<T> addChild(String name, T value)
{
TreeNode<T> child = new TreeNode<T>(name, value);
child.parent = this;
child.level = level + 1;
children.add(child);
return child;
}
/**
* 将子节点插入到指定位置。
* @param index 指定新增子节点在已有子节点中的位置。
* @param name 新增子节点的名称
* @param value 新增子节点的值
* @return 返回新增的子节点对象
*/
public TreeNode<T> addChild(int index, String name, T value)
{
TreeNode<T> child = new TreeNode<T>(name, value);
child.parent = this;
child.level = level + 1;
children.add(index, child);
return child;
}
/**
* 删除由uuid指定的子节点及其子节点。
* @param uuid 指定待删除节点的uuid。
* @return 若删除成功,则返回1;若uuid与根节点的uuid相同,则返回-1;若找不到指定的节点,则返回0。
*/
public int deleteChild(UUID uuid)
{
int deleteCount = 0;
if (this.uuid.equals(uuid) && (parent == null))
{
deleteCount = DCN_CANNOT_DELETE_ROOT;
}
else
{
TreeNode<T> node = findNode(uuid, parent);
if (node != null)
{
node.deleteChildren();
node.parent.children.remove(node);
deleteCount = 1;
}
else
{
deleteCount = 0;
}
}
return deleteCount;
}
/**
* 删除指定名称的节点及其子节点。
* @param name 指定待删除的节点的名称。
* @return 若删除成功,则返回删除的节点数(不含其子节点);若节点名称与根节点相同,则返回-1;若找不到指定的节点,则返回0。
*/
public int deleteChild(String name)
{
int deleteCount = 0;
if (this.name.equals(name) && (parent == null))
{
deleteCount = DCN_CANNOT_DELETE_ROOT;
}
else
{
Object[] nodesArray = findNode(name, parent).toArray();
deleteCount = nodesArray.length;
for(int index = 0; index < deleteCount; index ++)
{
@SuppressWarnings("unchecked")
TreeNode<T> node = (TreeNode<T>)nodesArray[deleteCount - index - 1]; // 反向迭代:要先删除下层节点,再删除上层节点。
node.deleteChildren(); // 先删除该节点的所有子节点。
node.parent.children.remove(node); // 删除节点自身。
}
}
return deleteCount;
}
/**
* 删除所有子节点。
*/
public void deleteChildren()
{
for(TreeNode<T> child : children)
{
child.deleteChildren();
}
children.clear();
}
/**
* 根据节点的名称找到该节点。
* @param name 节点的名称
* @param from 从哪个节点开始搜寻。若from为null,则从树的根节点开始搜寻。
* @return 具有相同名称的所有节点。
*/
public List<TreeNode<T>> findNode(String name, TreeNode<T> from)
{
List<TreeNode<T>> nodes = new Vector<>();
TreeNode<T> currNode = (from != null) ? from : this;
// System.out.println(currNode); __used_for_debug__
if (currNode.name.equals(name)) // 判断当前节点是否匹配
{
nodes.add(currNode);
}
// 在所有子节点中查找匹配的节点
for(TreeNode<T> child : currNode.children)
{
List<TreeNode<T>> matchedChildNodes = findNode(name, child);
if (matchedChildNodes != null)
{ // 若找到匹配的子节点(可能是多个),则这些匹配节点加入到节点列表中。
nodes.addAll(matchedChildNodes);
}
}
return (nodes.size() > 0) ? nodes : null;
}
/**
* 根据节点的ID找到节点。
* @param uuid 节点的uid
* @param from 从哪个节点开始搜寻。若from为null,则从树的根节点开始搜寻。
* @return uuid代表的节点。
*/
public TreeNode<T> findNode(UUID uuid, TreeNode<T> from)
{
TreeNode<T> node = null;
TreeNode<T> currNode = (from != null) ? from : this;
// System.out.println(currNode); __used_for_debug__
if (currNode.uuid.equals(uuid))
{
node = currNode;
}
else
{
List<TreeNode<T>> children = currNode.children;
for(TreeNode<T> child : children)
{
node = findNode(uuid, child);
if (node != null)
{
break;
}
}
}
return node;
}
/**
* 按深度优先遍历整颗树,按遍历顺序将树节点依次存放在列表中。
* @return 返回存放了深度优先遍历结果的树节点列表。
*/
public List<TreeNode<T>> walkByDepth()
{
List<TreeNode<T>> nodesList = new Vector<>();
nodesList.add(this); // 首先将自身加入到列表中。
for(TreeNode<T> child : children) // 对每一个子节点做深度优先遍历。
{
List<TreeNode<T>> childNodes = child.walkByDepth();
nodesList.addAll(childNodes);
}
return nodesList;
}
/**
* 按广度优先遍历整颗树,按遍历顺序将树节点依次存放在列表中。
* @return 返回存放了广度优先遍历结果的树节点列表。
*/
public List<TreeNode<T>> walkByBreadth()
{
List<TreeNode<T>> nodesList = new Vector<>();
Queue<TreeNode<T>> relayQueue = new LinkedList<>();
relayQueue.add(this);
while(!relayQueue.isEmpty())
{
TreeNode<T> node = relayQueue.poll();
nodesList.add(node);
relayQueue.addAll(node.children);
}
return nodesList;
}
/**
* 将树节点转换为字符串表达。
*/
public String toString()
{
return "level: " + level + ", name: " + name + ", value: " + value + ", uuid: " + uuid.toString();
}
static final public int DCN_CANNOT_DELETE_ROOT = -1;
/**
* 该方法仅用于测试。
* @param args 命令行参数
*/
public static void main(String args[])
{
/* create the tree. */
TreeNode<String> root = new TreeNode<String>("root", "0");
TreeNode<String> child = new TreeNode<String>("child-01", "1");
root.addChild(child);
child.addChild(new TreeNode<String>("child-01-01", "1-1"));
child.addChild(new TreeNode<String>("child-01-02", "1-2"));
child.addChild(new TreeNode<String>("child-01-03", "1-3"));
child = new TreeNode<String>("child-02", "2");
root.addChild(child);
child.addChild(new TreeNode<String>("child-02-01", "2-1"));
child.addChild(new TreeNode<String>("child-03-01", "2-2"));
child = new TreeNode<String>("child-03", "3");
root.addChild(child);
child.addChild(new TreeNode<String>("child-03-01", "3-1"));
TreeNode<String> grandson = new TreeNode<String>("child-03-02", "3-2");
child.addChild(grandson);
child.addChild("child-03-01", "3-3");
child.addChild("child-03-04", "3-4").addChild("child-03-04-01", "3-4-1");
child.addChild("child-03-05", "3-5").addChild("child-03-01", "3-5-1");
/* walk */
List<TreeNode<String>> allNodes = root.walkByDepth();
System.out.println("-----------------walk by depth-----------------");
for(TreeNode<String> node : allNodes)
{
System.out.println(node);
}
allNodes = root.walkByBreadth();
System.out.println("-----------------walk by breadth-----------------");
for(TreeNode<String> node : allNodes)
{
System.out.println(node);
}
/* find all neighbours. */
System.out.println("-----------get all neighbours-----------");
List<TreeNode<String>> neibs = child.getNeighbours();
for(TreeNode<String> neib : neibs)
{
System.out.println(neib);
}
/* find the soecified node by uuid */
System.out.println("-----------find matched node by uuid-----------");
TreeNode<String> nodeByUuid = root.findNode(grandson.uuid, null);
if (nodeByUuid != null)
{
System.out.println("found: " + nodeByUuid);
}
else
{
System.out.println("not found: " + grandson.uuid);
}
nodeByUuid = root.findNode(UUID.randomUUID(), null); // 随便弄个uuid,看看是否能匹配,结果自然是找不到。
if (nodeByUuid != null)
{
System.out.println("found: " + nodeByUuid);
}
else
{
System.out.println("not found: " + child.uuid);
}
/* find the specified node by name */
System.out.println("-----------find matched node by name-----------");
List<TreeNode<String>> nodesByName = root.findNode("child-03-01", null);
for(TreeNode<String> node : nodesByName)
{
System.out.println(node);
}
/* find root */
System.out.println("------------------find root node---------------");
TreeNode<String> rootNode = nodesByName.get(0).getRoot();
System.out.println(rootNode);
/* delete node by name */
System.out.println("------------delete node by name----------------");
int deleteCount = root.deleteChild("child-03-01");
System.out.println("delete count of 'child-03-01': " + deleteCount);
nodesByName = root.findNode("child-03-01", null);
System.out.println("count of 'child-03-01': " + ((nodesByName == null) ? 0 : nodesByName.size()));
/* delete node by uuid */
System.out.println("-------------delete node by uuid---------------");
UUID grandsonUuid = grandson.uuid;
deleteCount = root.deleteChild(grandsonUuid);
System.out.println("delete count of " + grandsonUuid + ": " + deleteCount);
nodeByUuid = root.findNode(grandsonUuid, root);
System.out.println("count of " + grandsonUuid + ": " + ((nodeByUuid == null) ? 0 : 1));
/* delete all children */
System.out.println("------------delete all children----------------");
root.deleteChildren();
System.out.println("count of children: " + root.children.size());
}
}
特点:
- 纵向无限层级,横向无限扩展
- 元素不排序,不同元素的名称和值均可相同,靠UUID区分不同元素
- 支持将新节点插入到当前节点的子节点列表的任意位置
- 支持将一个树插入到另一棵树种,前提是两棵树的节点值具有相同的类型
- 采用泛型编程,所以节点可以是任意类型
- 可以用getLevel()方法获得节点在树中的层级
- 支持深度优先和广度优先两种遍历方法
- 适用于分类树、目录树、XML的DOM树等允许节点内容重复的情形
限制:
- 不直接支持插入孙节点及更下层的节点,要先找到子节点,再为该子节点插入子节点
- 由于元素不排序,所以搜索效率不高,不适用于大规模树
- 未考虑线程安全问题,所以在多线程情况下慎用
- 树中所有节点的值必须是同一类型