Java数组转树(listToTree), 树转数组(treeToList)的正确姿势, 写成接口方式
直接上代码:
package cn.mandy.sysadmin.common.util;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
// MandyTree 实现 deepClone, 需要 Serializable
public interface MandyTree<T> extends Serializable {
Long getParentId();
void setParentId(Long parentId);
Long getId();
void setId(Long id);
List<T> getChildren();
void setChildren(List<T> children);
//java8 接口支持默认实现了
static <T extends MandyTree<T>> List<T> treeToList(List<T> tree) {
List<T> nodes = new ArrayList<>();
for (T node : tree) {
T newNode = deepClone(node);
newNode.setChildren(null);
nodes.add(newNode);
if (node.getChildren() != null && ! node.getChildren().isEmpty()) {
nodes.addAll(treeToList(node.getChildren()));
}
}
return nodes;
}
static <T extends MandyTree<T>> List<T> listToTree(List<T> nodes) {
List<T> tree = new ArrayList<>();
for (T node : nodes) {
T parent;
Long parentId = node.getParentId();
parent = findParent(parentId, tree);
if (null == parent){
//T newNode;
//tree.add(node); 有问题的代码,要用deepClone噢
tree.add(deepClone(node));
}else {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
//parent.getChildren().add(node);
parent.getChildren().add(deepClone(node));
}
}
return tree;
}
static <T extends MandyTree<T>> T findParent(Long parentId, List<T> nodeList) {
for (T it : nodeList) {
if (it.getId().equals(parentId)){
return it;
}else if (null != it.getChildren()){
T parentNode = findParent(parentId, it.getChildren());
if (null != parentNode){
return parentNode;
}
}
}
//nodeList.forEach();
return null;
}
//一定要深克隆, 不能用原来的入参中的引用, 否则, 你试试看 :)
static <T extends MandyTree<T>> T deepClone(T src) {
//static <T extends MandyTree<T>> T deepClone(T src) {
T dest = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(src);
//将流序列化成对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
dest = (T) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return dest;
}
}
使用方法:
MandyDepartment 是由 MyBatisGenerator 生成的 model, id, parentId 字段都定义为 BigInteger;
其它表也一样,遵守统一约定。否则就要自己写getter, setter去实现 MandyTree 中的接口函数了.
生成代码后, 类声明改一下,如下,
public class MandyDepartment implements MandyTree<MandyDepartment> {
private List<MandyDepartment> children;
再用IDE自动生成 Getter, Setter;
然后就可以愉快地使用了。
List<MandyDepartment> tree = MandyTree.listToTree(list);
注意这里还有一个约束条件, 就是List中, 父节点要排在子节点前面。
通常这个约束还是比较好办的, 表中id自增,父节点记录比子节点记录先一步插入,select 时 order by id 一下就可以了。
这样做比起用 tree.getPid() == 0 判断, 把这些节点当根节点先加入tree 的那种方式,
优点是你可以只得到部份数据(不一定包括表中的全部数据), 只得到一部份分枝,也能组成树。
我还看到有的童鞋用sql 语句 select a.*, b.* from table_name a left join table_name b on a.id = b.parent_id 的方法,
我认为更是不那么好的办法了。
新年了,因为新型肺炎宅在家里,写下首篇博文。
如果您看到这里,一起再为那些不幸染上新型肺炎的国人祈祷吧。
还有,祝福所有的人安康幸福,雪融冰消会有时, 春暖花开在明天!