通过指定code,parentCode 支持对不同
转换方法
package org.fffnfg.util.convert.enhancen;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.fffnfg.util.check.collect.CollectionUtils;
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<TreeDTO<T>, Integer> order;
private List<T> list;
public ConvertTreeUtils(Function<T, String> code, Function<T, String> parent, Function<TreeDTO<T>, Integer> order) {
this.code = code;
this.parent = parent;
this.order = order;
}
/**
* 转换为树结构
*
* @param list 元数据列表
* @return 树结构
*/
public List<TreeDTO<T>> getTreeList(List<T> list) {
if (Objects.isNull(isRoot)) {
log.debug("未自定义根节点判断方法,父节点为空<==>根节点");
}
this.list = list;
if (!CollectionUtils.isEmpty(list)) {
Map<String, TreeDTO<T>> resultMap = new HashMap<>(8);
for (T node : list) {
setMenus(node, resultMap);
}
return seekRootTrees(resultMap);
}
return new ArrayList<>(0);
}
/**
* 为当前节点设置层级关系
*
* @param node 当前节点
* @param resultMap 结果集
*/
private void setMenus(final T node, final Map<String, TreeDTO<T>> resultMap) {
setMenus(node, resultMap, 1);
}
private void setMenus(final T node, final Map<String, TreeDTO<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(code.apply(node)) && code.apply(node).equals(parent.apply(node))) {
log.error("不能指向自身;{}", node);
return;
}
TreeDTO<T> treeNode = resultMap.get(code.apply(node));
if (Objects.isNull(treeNode)) {
treeNode = getTreeDTO(node);
}
// 如果是根节点
if (isRootNode(node)) {
TreeDTO<T> parentApply = resultMap.get(parent.apply(node));
if (Objects.isNull(parentApply)) {
resultMap.put(code.apply(node), treeNode);
}
}
// 父节点不存在
else if (Objects.isNull(resultMap.get(parent.apply(node)))) {
// 获取父节点
T parentNode = getByCode(parent.apply(node));
if (Objects.nonNull(parentNode)) {
setMenus(parentNode, resultMap, ++level);
// 设置当前节点
TreeDTO<T> parentTree = getTreeDTO(parentNode);
parentTree.getChildren().add(treeNode);
resultMap.put(code.apply(parentNode), parentTree);
} else {
log.info("无父节点: {}", node);
}
} else {
TreeDTO<T> parentTree = resultMap.get(parent.apply(node));
// 设置当前节点
parentTree.getChildren().add(treeNode);
resultMap.put(code.apply(node), treeNode);
}
}
/**
* 是否为根节点
*
* @param node 当前节点
* @return 是否为根节点
*/
private boolean isRootNode(T node) {
return Objects.isNull(isRoot) ? Objects.isNull(parent.apply(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> TreeDTO<T> getTreeDTO(T node) {
TreeDTO<T> tree = new TreeDTO<>();
tree.setNode(node);
if (Objects.isNull(tree.getChildren())) {
tree.setChildren(new ArrayList<>(8));
}
return tree;
}
/**
* 字节点排序
*
* @param tTreeDTO 当前节点
*/
private void sort(TreeDTO<T> tTreeDTO) {
List<TreeDTO<T>> children = tTreeDTO.getChildren();
if (!CollectionUtils.isEmpty(children) && children.size() > 1) {
children.sort(Comparator.comparing(order));
for (TreeDTO<T> subChildren : children) {
sort(subChildren);
}
}
}
/**
* 从根节点开始寻找
*
* @param resultMap 结果集合
* @return 根节点列表
*/
private List<TreeDTO<T>> seekRootTrees(Map<String, TreeDTO<T>> resultMap) {
// map 转List 重排序
List<TreeDTO<T>> result = new ArrayList<>(resultMap.values());
result = result.stream().filter(tree -> Objects.isNull(isRoot) ?
Objects.isNull(parent.apply(tree.getNode())) :
isRoot.test(tree.getNode())).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(result) && result.size() > 1) {
result.sort(Comparator.comparing(order));
//子节点重排序
for (TreeDTO<T> tTreeDTO : result) {
sort(tTreeDTO);
}
}
return result;
}
}
树形结构实体类
package org.fffnfg.util.convert.enhancen;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Setter
@Getter
@ToString
public class TreeDTO<T> {
T node;
List<TreeDTO<T>> 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;
/**
* @author peng0806@foxmail.com
* @Date 2021/7/14 下午9:34
*/
public class ConvertTreeUtilsTest1 {
@Test
public void GetTreeList() {
Function<Node, String> code = node -> node.code;
Function<Node, String> parentCode = node -> node.parentCode;
Function<TreeDTO<Node>, Integer> order = node -> node.getNode().getOrder();
ConvertTreeUtils<Node> convertTreeUtils = new ConvertTreeUtils<>(code, parentCode, order);
int total = 50;
List<Node> list = new ArrayList<>(total);
setList(list);
// for (Node node : list) {
// System.out.println(JSONObject.toJSON(node));
// }
//
List<TreeDTO<Node>> res = convertTreeUtils.getTreeList(list);
System.out.println("========= total\t" + res.size());
System.out.println(JSONObject.toJSON(res));
// for (TreeDTO<Node> re : res) {
// }
}
private void setList(List<Node> list) {
Node node9 = new Node();
node9.setId(9);
node9.setCode("left9");
node9.setParentCode("sub8");
node9.setName("code1");
node9.setOrder(1);
Node node8 = new Node();
node8.setId(8);
node8.setCode("sub8");
node8.setParentCode("group4");
node8.setName("code1");
node8.setOrder(3);
Node node7 = new Node();
node7.setId(7);
node7.setCode("sub7");
node7.setParentCode("sub6");
node7.setName("code1");
node7.setOrder(1);
Node node6 = new Node();
node6.setId(6);
node6.setCode("sub6");
node6.setParentCode("group3");
node6.setName("code1");
node6.setOrder(1);
Node node5 = new Node();
node5.setId(5);
node5.setCode("group5");
node5.setParentCode("group3");
node5.setName("code1");
node5.setOrder(1);
Node node4 = new Node();
node4.setId(4);
node4.setCode("group4");
node4.setParentCode("group3");
node4.setName("group1");
node4.setOrder(1);
Node node3 = new Node();
node3.setId(3);
node3.setCode("group3");
node3.setParentCode("root1");
node3.setName("root3");
node3.setOrder(1);
Node node2 = new Node();
node2.setId(2);
node2.setCode("root2");
node2.setParentCode(null);
node2.setName("root2");
node2.setOrder(1);
Node node1 = new Node();
node1.setId(1);
node1.setCode("root1");
node1.setParentCode(null);
node1.setName("root1");
node1.setOrder(1);
Node node = new Node();
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);
}
}
测试使用的待转换类
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;
}