前言
树状构造是咱们在日常工作中常常须要返回的数据结构 一个小小的数状构造也能看出一个开发者的思维形式 我集体最开始写这种树状构造真的是代码老长了 他人一看就会晓得我是一个菜鸟 缓缓的本人思考怎么去用起码的代码去搭建一个树状构造 也做一些整顿 编码思路真的很重要 思路好代码才会简洁强壮筹备实体类咱们先定义一个实体类 不便演示 第一种办法的算是和结构器相互依赖
Node.class:import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
- @author 变成派大星
*/
@Data
public class Node {
private Integer Id;
private String name;
private Integer pid;
private List<Node> treeNode = new ArrayList<>();
public Node(int id, int pid) {
this.Id = id;
this.pid = pid;
}
public Node(int id, int pid, String name) {this(id, pid);
this.name = name;
}
}
复制代码思路一咱们的树状图个别都是一个动静的构造也就是说咱们不能把代码写的太死板
无论树的深度怎么减少咱们都不须要更改代码 一般来说 树状构造都有一个外围字段 pid 第一个节点
都是为 0 第二个节点的 Pid 是第一个节点的标识咱们思路一就是从这边登程先将第一层节点找进去剩下的节点通过 Pid 进行分组 生成一个 Map pid 作为 Key Node
作为 Value 通过循环汇合 去 Map 外面取 Key 对应的节点 Set 到本人的子节点上面去除数据中第一层 Pid
!= 0 的数据 4.1
如下图
代码演示 这个须要看一下代码逻辑 /**
- @author 变成派大星
*/
@Service
@AllArgsConstructor
public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements NodeService {
private NodeMapper nodeMapper;
/**
* @return 进行树结构的解决操作
*/
@Override
public List<Node> handleTreeVo() {Node first = new Node(1, 0, "first");
Node second = new Node(2, 1, "second");
Node third = new Node(3, 2, "third");
Node second001 = new Node(4, 1, "second001");
Node third001 = new Node(5, 4, "third001");
// 组装树状数据
List<Node> nodes = Arrays.asList(first,second,third,second001,third001);
return buildTree(nodes);
}
public List<Node> buildTree(List<Node> nodes) {
// 将这些非顶级节点的数据按 pid 进行分组 这个是依据 pid 为 key 第一步过滤非 Pid= 0 的节点 第二步进行分组
Map<Integer, List<Node>> nodeMap = nodes.stream().filter(node->node.getPid()!=0)
.collect(Collectors.groupingBy(node -> node.getPid()));
// 循环设置对应的子节点(依据 id = pid)上一步以 pid 为 Key 所以就间接循环获取
nodes.forEach(node -> node.setTreeNode(nodeMap.get(node.getId())));
// 过滤第一层不是 Pid 为零的数据 也就是没有根节点的数据
List<Node> treeNode = nodes.stream().filter(node -> node.getPid() == 0).collect(Collectors.toList());
return treeNode;
}
}
复制代码后果
小总结下面这种写法
几行代码就能生成一个树状数据 也算一种思路长处的话就是代码简略简洁要说毛病 可能最初一步有
点不难受 也是须要改良的中央 而且这个是专门配合的实体类 真正开发中会比这麻烦点
前端也喜爱后端开发给的树状图字段名都是一样的 所有个别都要写一个 Vo 返回类 这个配合返回类
麻烦水平也会减少思路二因为树状数据比拟罕用 咱们每次都写一个办法必定比拟笨 咱们能够利用泛
型区写一个工具类 我先把代码写进去而后再解释 这个是能够间接应用的 是比拟通用的 import
cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
- @author 变成派大星
*/
public class TreeUtils {
/**
* @param list 源数据
* @param setChildListFn 设置递归的办法
* @param idFn 获取 id 的办法
* @param pidFn 获取父 id 的办法
* @param getRootCondition 获取根节点的提哦啊见
* @return 将 List 转换成 Tree
*/
public static <M, T> List<M> listToTree(List<M> list,
Function<M, T> idFn,
Function<M, T> pidFn,
BiConsumer<M, List<M>> setChildListFn,
Predicate<M> getRootCondition) {if (CollUtil.isEmpty(list)) return null;
Map<T, List<M>> listMap = list.stream().collect(Collectors.groupingBy(pidFn));
list.forEach(model -> setChildListFn.accept(model, listMap.get(idFn.apply(model))));
return list.stream().filter(getRootCondition).collect(Collectors.toList());
}
public static <M> List<M> treeToList(List<M> source,
Function<M, List<M>> getChildListFn,
BiConsumer<M, List<M>> setChildListFn,
Predicate<M> getRootCondition) {List<M> target = new ArrayList<>();
if (CollectionUtils.isNotEmpty(source)) {treeToList(source, target, getChildListFn);
target.forEach(model -> setChildListFn.accept(model, null));
target.addAll(target.stream().filter(getRootCondition).collect(Collectors.toList()));
}
return target;
}
private static <F> void treeToList(List<F> source,
List<F> target,
Function<F, List<F>> getChildListFn) {if (CollectionUtils.isNotEmpty(source)) {target.addAll(source);
source.forEach(model -> {List<F> childList = getChildListFn.apply(model);
if (CollectionUtils.isNotEmpty(childList)) {treeToList(childList, target, getChildListFn);
}
});
}
}
}
复制代码办法一:listToTree 就是将你的数据从 list 转换成 Tree 构造 public List<Node> handleTree(){
Node first = new Node(1, 0, "first");
Node second = new Node(2, 1, "second");
Node third = new Node(3, 2, "third");
Node second001 = new Node(4, 1, "second001");
Node third001 = new Node(5, 4, "third001");
List<Node> nodes = Arrays.asList(first,second,third,second001,third001);
// 只须要一行数据
return TreeUtils.listToTree(nodes, Node::getId, Node::getPid, Node::setTreeNode, (l) -> l.getPid() == 0);
}
复制代码后果
这个其实十分不便 基本上一分钟就能转换了 再也不会被前端催了 也能够本人去改写 依照本人的需要去调整 思路很像 只不过是去优化办法二 Tree 转 Listpublic List<Node> handleTree(){
Node first = new Node(1, 0, "first");
Node second = new Node(2, 1, "second");
Node third = new Node(3, 2, "third");
Node second001 = new Node(4, 1, "second001");
Node third001 = new Node(5, 4, "third001");
List<Node> nodes = Arrays.asList(first,second,third,second001,third001);
List<Node> nodeList = TreeUtils.listToTree(nodes, Node::getId, Node::getPid, Node::setTreeNode, (l) -> l.getPid() == 0);
// 树状构造转换成 List 也就是还原数据
return TreeUtils.treeToList(nodeList, Node::getTreeNode, Node::setTreeNode, (l) -> l.getPid() == 0);
}
复制代码能够去反转树结构 用途的看本人的业务需要 能够看一下思路
总结兴许有很多类去实现树结构 只须要很少的代码 然而有些思路还是要学的 也有其余暴力组成 然而基本思路差不多 如果各位大佬有更好的思路 能够揭示一下 祝顺利