最近写的功能总是需要处理树结构的数据,用递归写了几个通用树节点处理方法
1、定义树节点
import lombok.Data;
import java.util.List;
@Data
public class TreeNode<T> {
private String id;
private String nodeId;
private String nodeName;
private String nodePath;
private T data;
private List<TreeNode<T>> children;
}
2、工具类
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class TreeNodeUtil {
/**
* 创建节点id
* @param list 树节点数据
*/
public static <T> void generateNodeId(List<TreeNode<T>> list) {
handlerTreeNode(list, tTreeNode -> {
tTreeNode.setNodeId(UUID.randomUUID().toString().replace("-", ""));
generateNodeId(tTreeNode.getChildren());
});
}
/**
* 创建节点路径
* @param list 树节点数据
* @param parentPath 父路径
*/
public static <T> void generateNodePath(List<TreeNode<T>> list, String parentPath) {
handlerTreeNodeForI(list, (treeNode, integer) -> {
String currentPath = parentPath + String.format("%02d", (integer + 1));
treeNode.setNodePath(currentPath);
generateNodePath(treeNode.getChildren(), currentPath);
});
}
/**
* 通过forEach处理每个节点点数据
* @param list 树节点数据
* @param consumer 处理节点消费者,由用户提供
*/
public static <T> void handlerTreeNode(List<TreeNode<T>> list, Consumer<TreeNode<T>> consumer) {
if (list == null) {
return;
}
list.forEach(item -> {
consumer.accept(item);
// 自递归继续处理子集
handlerTreeNode(item.getChildren(), consumer);
});
}
/**
* 通过fori处理每个节点,并获取当前节点处于children的下标
* @param list 树节点数据
* @param biConsumer 处理节点的消费者
*/
public static <T> void handlerTreeNodeForI(List<TreeNode<T>> list, BiConsumer<TreeNode<T>, Integer> biConsumer) {
if (list == null) {
return;
}
for (int i = 0; i < list.size(); i++) {
TreeNode<T> treeNode = list.get(i);
biConsumer.accept(treeNode, i);
// 自递归继续处理子集
handlerTreeNodeForI(treeNode.getChildren(), biConsumer);
}
}
/**
* 过滤树节点
* @param list 树节点数据
* @param needChild 断言不通过时,是否需要不通过节点的子集
* @param predicate 断言
* @return 过滤后的树节点数据
*/
public static <T> List<TreeNode<T>> filter(List<TreeNode<T>> list, boolean needChild, Predicate<TreeNode<T>> predicate) {
if (list == null) {
return null;
}
List<TreeNode<T>> result = new ArrayList<>();
// 收集需要递归的对象
list.forEach(item -> {
if (predicate.test(item)) {
// 通过判断的节点加入result
result.add(item);
} else if (needChild) {
// 不通过判断但是是否需要子集
result.addAll(item.getChildren());
}
});
// 递归继续执行
result.forEach(item -> item.setChildren(filter(item.getChildren(), needChild,predicate)));
return result;
}
public static void main(String[] args) {
TreeNode<Integer> treeNode1 = new TreeNode<>();
treeNode1.setData(1);
treeNode1.setNodeName("节点1");
TreeNode<Integer> treeNode2 = new TreeNode<>();
treeNode2.setData(2);
treeNode2.setNodeName("节点2");
TreeNode<Integer> treeNode3 = new TreeNode<>();
treeNode3.setData(3);
TreeNode<Integer> treeNode4 = new TreeNode<>();
treeNode4.setData(4);
treeNode4.setNodeName("节点4");
List<TreeNode<Integer>> children = new ArrayList<>();
children.add(treeNode2);
children.add(treeNode3);
treeNode1.setChildren(children);
List<TreeNode<Integer>> children1 = new ArrayList<>();
children1.add(treeNode4);
treeNode3.setChildren(children1);
List<TreeNode<Integer>> treeNodes = new ArrayList<>();
treeNodes.add(treeNode1);
// handlerTreeNode(treeNodes, treeNode -> System.out.println(treeNode.getData()));
// List<TreeNode<Integer>> result = handlerTreeNodeAndCollect(treeNodes, treeNode -> {
// String nodeName = treeNode.getNodeName();
// if (nodeName == null || "".equals(nodeName)) {
// return treeNode.getChildren();
// } else {
// return Arrays.asList(treeNode);
// }
// });
List<TreeNode<Integer>> result = filter(treeNodes, true, treeNode -> {
String nodeName = treeNode.getNodeName();
return nodeName != null && !"".equals(nodeName);
});
result.forEach(System.out::println);
}
}
主要写了3个方法:
1、通过forEach处理每个节点数据,用于生成节点id
2、通过fori处理每个节点数据,用于生成节点路径
3、过滤节点,当某个节点不满足所给条件判断是否需要当前节点的子集,过滤出树中满足条件的所有节点