2021-07-15

list转tree的工具类

注意事项

  1. order安装从小到大排序,可以通过策略设置,一般为空设置返回int最大值
  2. 使用需要新建视图DTO 继承需要转换的实体类,并实现工具类中的IChildren 接口,默认子节点名称为children 如果需要其他类则通过实现方法覆盖。

实体类

package org.fffnfg.util.convert.enhancen;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Node {
    Integer id;
    String name;
    String code;
    String parentCode;
    Integer order;
}

转换后的类

@Getter
@Setter
public class TreeNode extends Node implements ConvertTreeUtils.IChildren<TreeNode> {
    List<TreeNode> children;
}

测试方法

package org.fffnfg.util.convert.enhancen;

import com.alibaba.fastjson.JSONObject;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * @Date 2021/7/14 下午9:34
 */
public class ConvertTreeUtilsTest1 {
    @Test
    public void GetTreeList() {
        Function<TreeNode, String> code = node -> node.code;
        Function<TreeNode, String> parentCode = node -> node.parentCode;
        Function<TreeNode, Integer> order = node -> node.getOrder();
        ConvertTreeUtils<TreeNode> convertTreeUtils = new ConvertTreeUtils<>(code, parentCode, order);
        int total = 50;
        List<TreeNode> list = new ArrayList<>(total);
        setList(list);
        List<TreeNode> res = convertTreeUtils.getTreeList(list);
        System.out.println("========= total\t" + res.size());
        System.out.println(JSONObject.toJSON(res));
    }

    private void setList(List<TreeNode> list) {
        TreeNode node9 = new TreeNode();
        node9.setId(9);
        node9.setCode("left9");
        node9.setParentCode("sub8");
        node9.setName("code1");
        node9.setOrder(1);
        TreeNode node8 = new TreeNode();
        node8.setId(8);
        node8.setCode("sub8");
        node8.setParentCode("dept4");
        node8.setName("code1");
        node8.setOrder(3);
        TreeNode node7 = new TreeNode();
        node7.setId(7);
        node7.setCode("sub7");
        node7.setParentCode("sub6");
        node7.setName("code1");
        node7.setOrder(1);
        TreeNode node6 = new TreeNode();
        node6.setId(6);
        node6.setCode("sub6");
        node6.setParentCode("dept3");
        node6.setName("code1");
        node6.setOrder(1);
        TreeNode node5 = new TreeNode();
        node5.setId(5);
        node5.setCode("dept5");
        node5.setParentCode("dept3");
        node5.setName("code1");
        node5.setOrder(1);
        TreeNode node4 = new TreeNode();
        node4.setId(4);
        node4.setCode("dept4");
        node4.setParentCode("dept3");
        node4.setName("dept1");
        node4.setOrder(1);
        TreeNode node3 = new TreeNode();
        node3.setId(3);
        node3.setCode("dept3");
        node3.setParentCode("root1");
        node3.setName("root3");
        node3.setOrder(1);
        TreeNode node2 = new TreeNode();
        node2.setId(2);
        node2.setCode("root2");
        node2.setParentCode(null);
        node2.setName("root2");
        node2.setOrder(1);
        TreeNode node1 = new TreeNode();
        node1.setId(1);
        node1.setCode("root1");
        node1.setParentCode(null);
        node1.setName("root1");
        node1.setOrder(1);
        TreeNode node = new TreeNode();
        node.setId(0);
        node.setCode("root0");
        node.setParentCode(null);
        node.setName("root0");
        node.setOrder(1);
        list.add(node7);
        list.add(node8);
        list.add(node9);
        list.add(node4);
        list.add(node5);
        list.add(node6);
        list.add(node);
        list.add(node1);
        list.add(node2);
        list.add(node3);
    }
}

测试结果

2:43:49.091 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - 未自定义根节点判断方法,父节点为空<==>根节点
22:43:49.097 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=7, name=code1, code=sub7, parentCode=sub6, order=1)
22:43:49.101 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--|--Node(id=6, name=code1, code=sub6, parentCode=dept3, order=1)
22:43:49.101 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--|--|--Node(id=3, name=root3, code=dept3, parentCode=root1, order=1)
22:43:49.101 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--|--|--|--Node(id=1, name=root1, code=root1, parentCode=null, order=1)
22:43:49.101 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=8, name=code1, code=sub8, parentCode=dept4, order=3)
22:43:49.101 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--|--Node(id=4, name=dept1, code=dept4, parentCode=dept3, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=9, name=code1, code=left9, parentCode=sub8, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--|--Node(id=8, name=code1, code=sub8, parentCode=dept4, order=3)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=4, name=dept1, code=dept4, parentCode=dept3, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=5, name=code1, code=dept5, parentCode=dept3, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=6, name=code1, code=sub6, parentCode=dept3, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=0, name=root0, code=root0, parentCode=null, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=1, name=root1, code=root1, parentCode=null, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=2, name=root2, code=root2, parentCode=null, order=1)
22:43:49.102 [main] DEBUG org.fffnfg.util.convert.enhancen.ConvertTreeUtils - |--Node(id=3, name=root3, code=dept3, parentCode=root1, order=1)
========= total	3
[
  {
    "code": "root0",
    "children": [],
    "name": "root0",
    "id": 0,
    "order": 1
  },
  {
    "code": "root1",
    "children": [
      {
        "code": "dept3",
        "children": [
          {
            "code": "sub6",
            "children": [
              {
                "code": "sub7",
                "children": [],
                "parentCode": "sub6",
                "name": "code1",
                "id": 7,
                "order": 1
              }
            ],
            "parentCode": "dept3",
            "name": "code1",
            "id": 6,
            "order": 1
          },
          {
            "code": "dept4",
            "children": [
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              },
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              }
            ],
            "parentCode": "dept3",
            "name": "dept1",
            "id": 4,
            "order": 1
          },
          {
            "code": "dept4",
            "children": [
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              },
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              }
            ],
            "parentCode": "dept3",
            "name": "dept1",
            "id": 4,
            "order": 1
          },
          {
            "code": "dept5",
            "children": [],
            "parentCode": "dept3",
            "name": "code1",
            "id": 5,
            "order": 1
          },
          {
            "code": "sub6",
            "children": [
              {
                "code": "sub7",
                "children": [],
                "parentCode": "sub6",
                "name": "code1",
                "id": 7,
                "order": 1
              }
            ],
            "parentCode": "dept3",
            "name": "code1",
            "id": 6,
            "order": 1
          }
        ],
        "parentCode": "root1",
        "name": "root3",
        "id": 3,
        "order": 1
      },
      {
        "code": "dept3",
        "children": [
          {
            "code": "sub6",
            "children": [
              {
                "code": "sub7",
                "children": [],
                "parentCode": "sub6",
                "name": "code1",
                "id": 7,
                "order": 1
              }
            ],
            "parentCode": "dept3",
            "name": "code1",
            "id": 6,
            "order": 1
          },
          {
            "code": "dept4",
            "children": [
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              },
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              }
            ],
            "parentCode": "dept3",
            "name": "dept1",
            "id": 4,
            "order": 1
          },
          {
            "code": "dept4",
            "children": [
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              },
              {
                "code": "sub8",
                "children": [
                  {
                    "code": "left9",
                    "children": [],
                    "parentCode": "sub8",
                    "name": "code1",
                    "id": 9,
                    "order": 1
                  }
                ],
                "parentCode": "dept4",
                "name": "code1",
                "id": 8,
                "order": 3
              }
            ],
            "parentCode": "dept3",
            "name": "dept1",
            "id": 4,
            "order": 1
          },
          {
            "code": "dept5",
            "children": [],
            "parentCode": "dept3",
            "name": "code1",
            "id": 5,
            "order": 1
          },
          {
            "code": "sub6",
            "children": [
              {
                "code": "sub7",
                "children": [],
                "parentCode": "sub6",
                "name": "code1",
                "id": 7,
                "order": 1
              }
            ],
            "parentCode": "dept3",
            "name": "code1",
            "id": 6,
            "order": 1
          }
        ],
        "parentCode": "root1",
        "name": "root3",
        "id": 3,
        "order": 1
      }
    ],
    "name": "root1",
    "id": 1,
    "order": 1
  },
  {
    "code": "root2",
    "children": [],
    "name": "root2",
    "id": 2,
    "order": 1
  }
]

工具类

package org.fffnfg.util.convert.enhancen;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.fffnfg.util.check.exception.BusinessException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Slf4j
public class ConvertTreeUtils<T> {

    /**
     * 是否为根节点
     */
    @Setter
    private Predicate<T> isRoot;
    private Function<T, String> code;
    private Function<T, String> parent;
    private Function<T, Integer> order;
    private List<T> list;

    public ConvertTreeUtils(Function<T, String> code, Function<T, String> parent, Function<T, Integer> order) {
        this.code = code;
        this.parent = parent;
        this.order = order;
    }

    /**
     * 转换为树结构
     *
     * @param list 元数据列表
     * @return 树结构
     */
    public List<T> getTreeList(List<T> list) {
        if (Objects.isNull(isRoot)) {
            log.debug("未自定义根节点判断方法,父节点为空<==>根节点");
        }
        this.list = list;
        if (!CollectionUtils.isEmpty(list)) {
            Map<String, T> resultMap = new HashMap<>(8);
            for (T node : list) {
                if (node instanceof IChildren) {
                    setMenus(node, resultMap);
                } else {
                    log.debug("node需要实现 IChildren 接口");
                }
            }
            return seekRootTrees(resultMap);
        }
        return new ArrayList<>(0);
    }

    /**
     * 为当前节点设置层级关系
     *
     * @param node      当前节点
     * @param resultMap 结果集
     */
    private void setMenus(final T node, final Map<String, T> resultMap) {
        setMenus(node, resultMap, 1);
    }

    private void setMenus(final T node, final Map<String, T> resultMap, int level) {
        if (log.isDebugEnabled()) {
            StringBuilder step = new StringBuilder();
            for (int i = 0; i < level; i++) {
                step.append("|--");
            }
            log.debug("{}", step.append(node));
        }
        if (Objects.isNull(node)) {
            log.info("node 不能为空!");
            return;
        } else if (Objects.nonNull(getCode(node)) && getCode(node).equals(getParentCode(node))) {
            log.error("不能指向自身;{}", node);
            return;
        }
        T treeNode = resultMap.get(getCode(node));
        if (Objects.isNull(treeNode)) {
            treeNode = getTreeDTO(node);
        }
        // 如果是根节点
        if (isRootNode(node)) {
            T parentApply = resultMap.get(getParentCode(node));
            if (Objects.isNull(parentApply)) {
                resultMap.put(getCode(node), treeNode);
            }
        }
        // 父节点不存在
        else if (Objects.isNull(resultMap.get(getParentCode(node)))) {
            // 获取父节点
            T parentNode = getByCode(getParentCode(node));
            if (Objects.nonNull(parentNode)) {
                setMenus(parentNode, resultMap, ++level);
                // 设置当前节点
                T parentTree = getTreeDTO(parentNode);
                getTree(parentTree).getChildren().add(treeNode);
                resultMap.put(code.apply(parentNode), parentTree);
            } else {
                log.info("无父节点: {}", node);
            }
        } else {
            T parentTree = resultMap.get(getParentCode(node));
            // 设置当前节点
            getTree(parentTree).getChildren().add(treeNode);
            resultMap.put(getCode(node), treeNode);
        }
    }


    /**
     * 是否为根节点
     *
     * @param node 当前节点
     * @return 是否为根节点
     */
    private boolean isRootNode(T node) {
        return Objects.isNull(isRoot) ? Objects.isNull(getParentCode(node)) : isRoot.test(node);
    }

    /**
     * 在list中找到code对应的值
     *
     * @param code 节点编码
     * @return 列表中的节点
     */
    private T getByCode(String code) {
        for (T t : list) {
            if (code.equals(this.code.apply(t))) {
                return t;
            }
        }
        return null;
    }

    /**
     * @param node 节点
     * @param <T>  节点类
     * @return 包装树形结构
     */
    private <T> T getTreeDTO(T node) {
        IChildren<T> tree = (IChildren<T>) node;
        if (Objects.isNull(tree.getChildren())) {
            tree.setChildren(new ArrayList<>(8));
        }
        return (T) tree;
    }

    /**
     * 字节点排序
     *
     * @param tTreeDTO 当前节点
     */
    private void sort(T tTreeDTO) {
        IChildren<T> ch = (IChildren<T>) tTreeDTO;
        List<T> children = ch.getChildren();
        if (!CollectionUtils.isEmpty(children) && children.size() > 1) {
            children.sort(Comparator.comparing(order));
            for (T subChildren : children) {
                sort(subChildren);
            }
        }
    }

    /**
     * 从根节点开始寻找
     *
     * @param resultMap 结果集合
     * @return 根节点列表
     */
    private List<T> seekRootTrees(Map<String, T> resultMap) {

        // map 转List 重排序
        List<T> result = new ArrayList<>(resultMap.values());
        result = result.stream().filter(tree -> Objects.isNull(isRoot) ?
                Objects.isNull(parent.apply(tree)) :
                isRoot.test(tree)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(result)) {
            if (Objects.nonNull(order)) {
                result.sort(Comparator.comparing(order));
            }
            //子节点重排序
            for (T tTreeDTO : result) {
                sort(tTreeDTO);
            }
        }
        return result;
    }

    /**
     * 获取code
     *
     * @param node 当前节点
     * @return code
     */
    private String getCode(T node) {
        String inCode = code.apply(node);
        if (StringUtils.isEmpty(inCode)) {
            throw new BusinessException(String.format("code不能为空: %s", node));
        }
        return inCode;
    }

    /**
     * 获取父节点code
     *
     * @param node 当前节点
     * @return 父节点code
     */
    private String getParentCode(T node) {
        return parent.apply(node);
    }

    /**
     * 转换为树结构
     *
     * @param t
     * @return
     */
    private IChildren<T> getTree(T t) {
        return (IChildren<T>) t;
    }

    /**
     * 树形结构的必要方法
     *
     * @param <T> 具体的节点类型
     */
    public static interface IChildren<T> {

        /**
         * 设置子节点
         *
         * @param children 子节点
         */
        void setChildren(List<T> children);

        /**
         * 查询子节点
         *
         * @return 子节点列表
         */

        List<T> getChildren();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值