问题
场景:许多同学都遇到过将nodeLsit生成nodeTree的情况(节点列表拼接成树),但是经常是遇到一个新节点(新的类)就重写一个 listTotree 方法。代码逻辑一致,致冗余了。
解决
有两种方法减少此冗余,如下
方法一
1、提供一个node接口
2、节点对象实现此node
3、实现node对象的 toTree方法
代码大致如下:
//公共的节点接口
public interface MyNode{
//获取节点id
Object getNodeId();
//获取节点父id
Object getNodeParentId();
//获取节点子列表
List<? extends MyNode> getNodeChildren();
//设置子
void setNodeChildren(List nodes);
//获取节点序号
Comparable getNodeOrder();
}
//某对象
public class Bean implements MyNode{
Integer id;
Integer pId;
Integer order;
List<Bean> son;
//下列为业务信息
String info;
@Override
public Object getNodeId() {
return this.id;
}
@Override
public Object getNodeParentId() {
return this.pId;
}
@Override
public List<? extends MyNode> getNodeChildren() {
return this.son;
}
@Override
public Comparable getNodeOrder() {
return this.order;
}
@Override
public void setNodeChildren(List nodes) {
this.son = nodes;
}
}
//实现node方法
public List list2Tree(List<? extends MyNode> nodes){
List<Node> res = new ArrayList<>();
//TODO nodes 转化成树 res
return res;
}
//测试
@Test
public void test2Tree(){
List<Bean> nodes = null;
//TODO 初始化nodes
List<Bean> list = list2Tree(nodes);
}
方法二
利用jdk8的特性,进行抽象
估计说的太抽象大家不理解,方法二我写了详细代码,并提供测试结果
方法二代码
/** 工具代码 */
package com.xxx.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class ListToTreeUtil {
// list转树,不排序
public static <T> List<T> list2Tree(List<T> list,
Function<T, Serializable> getId,
Function<T, Serializable> getParentId,
Function<T, List<T>> getChildren,
BiConsumer<T, List<T>> setChildren,
Object root){
return list2Tree(list, getId, getParentId, getChildren, setChildren, root, null);
}
// list转树,可排序
public static <T> List<T> list2Tree(List<T> list,
Function<T, Serializable> getId,
Function<T, Serializable> getParentId,
Function<T, List<T>> getChildren,
BiConsumer<T, List<T>> setChildren,
Object root,
Function<T, Comparable> getOrder){
List<T> treeList = new ArrayList<>();
for(T tree : list){
//check root
if(objEquals(root, getParentId.apply(tree))){
treeList.add(tree);
}
//find tree's Children
for(T treeNode : list){
if(objEquals(getId.apply(tree), getParentId.apply(treeNode))){
if(getChildren.apply(tree) == null){
setChildren.accept(tree, new ArrayList<T>());
}
getChildren.apply(tree).add(treeNode);
}
}
if( getOrder != null){
List<T> childList = getChildren.apply(tree);
if( childList != null && childList.size() > 1){
List<T> sortList = getSortList(childList, getOrder);
setChildren.accept(tree, sortList);
}
}
}
return treeList;
}
//对比对象是否相等
public static boolean objEquals(Object a, Object b){
if(a == null)
return b == null;
return a.equals(b);
}
//list排序
public static <T> List<T> getSortList(List<T> list,Function<T, Comparable> getOrder){
List<T> sortList = new ArrayList<>();
List<T> nullOrderList = new ArrayList<>();
for(T item : list){
if(null == getOrder.apply(item)){
nullOrderList.add(item);
}else {
sortList.add(item);
}
}
sortList.sort((a,b) -> getOrder.apply(a).compareTo(getOrder.apply(b)));
sortList.addAll(nullOrderList);
return sortList;
}
}
方法二测试
/** bean */
//Node对象
@Data
@AllArgsConstructor
class Node{
Integer id;
Integer pId;
Integer order;
List<Node> son;
}
//Node2对象
@Data
@AllArgsConstructor
class Node2{
String id2;
String pId2;
String order2;
List<Node2> son2;
}
/** 方法二 测试代码 */
@Test
public void tree2list2(){
//测试Node对象,且排序
List<Node> list = Arrays.asList(new Node(0, null, null, null),
new Node(1, 0, -1, null),
new Node(2, 0, -2, null),
new Node(11, 1,-11, null),
new Node(12, 1,-12, null),
new Node(21, 2,-21, null),
new Node(22, 2,-22, null),
new Node(31, 3,-31, null)
);
List<Node> tree = ListToTreeUtil.list2Tree(list, Node::getId, Node::getPId, Node::getSon, Node::setSon, null, Node::getOrder);
System.out.println(JSON.toJSONString(tree));
//测试Node2对象,但选择不排序
List<Node2> listNode2 = Arrays.asList(
new Node2("0" , null, null, null),
new Node2("1" , "0", "a1", null),
new Node2("2" , "0", "a", null),
new Node2("11", "1", "b1", null),
new Node2("12", "1", "b", null),
new Node2("21", "2", "c1", null),
new Node2("22", "2", "c", null),
new Node2("31", "3", "d", null)
);
List<Node2> treeNode2 = ListToTreeUtil.list2Tree(listNode2, Node2::getId2, Node2::getPId2, Node2::getSon2, Node2::setSon2, null, null);
System.out.println(JSON.toJSONString(treeNode2));
}
方法二测试结果
//执行结果如下
[{"id":0,"son":[{"id":2,"order":-2,"pId":0,"son":[{"id":22,"order":-22,"pId":2},{"id":21,"order":-21,"pId":2}]},{"id":1,"order":-1,"pId":0,"son":[{"id":12,"order":-12,"pId":1},{"id":11,"order":-11,"pId":1}]}]}]
[{"id2":"0","son2":[{"id2":"1","order2":"a1","pId2":"0","son2":[{"id2":"11","order2":"b1","pId2":"1"},{"id2":"12","order2":"b","pId2":"1"}]},{"id2":"2","order2":"a","pId2":"0","son2":[{"id2":"21","order2":"c1","pId2":"2"},{"id2":"22","order2":"c","pId2":"2"}]}]}]
Process finished with exit code 0
node树的可视化如下
node2树的可视化如下
区别
-
方法一(节点接口)
优点:一次实现多次调用;便于理解。
缺点:有一定的代码侵入。 -
方法二(jdk8特性)
优点:无代码侵入。
缺点:使用有一定难度,初学者不好理解。