list转tree的工具类
注意事项
- order安装从小到大排序,可以通过策略设置,一般为空设置返回int最大值
- 使用需要新建视图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;
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;
}
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);
}
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);
}
}
private boolean isRootNode(T node) {
return Objects.isNull(isRoot) ? Objects.isNull(getParentCode(node)) : isRoot.test(node);
}
private T getByCode(String code) {
for (T t : list) {
if (code.equals(this.code.apply(t))) {
return t;
}
}
return null;
}
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;
}
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);
}
}
}
private List<T> seekRootTrees(Map<String, T> resultMap) {
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;
}
private String getCode(T node) {
String inCode = code.apply(node);
if (StringUtils.isEmpty(inCode)) {
throw new BusinessException(String.format("code不能为空: %s", node));
}
return inCode;
}
private String getParentCode(T node) {
return parent.apply(node);
}
private IChildren<T> getTree(T t) {
return (IChildren<T>) t;
}
public static interface IChildren<T> {
void setChildren(List<T> children);
List<T> getChildren();
}
}