前言
树状构造是咱们在日常工作中常常须要返回的数据结构 一个小小的数状构造也能看出一个开发者的思维形式 我集体最开始写这种树状构造真的是代码老长了 他人一看就会晓得我是一个菜鸟 缓缓的本人思考怎么去用起码的代码去搭建一个树状构造 也做一些整顿 编码思路真的很重要 思路好代码才会简洁强壮筹备实体类咱们先定义一个实体类 不便演示 第一种办法的算是和结构器相互依赖
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 进行树结构的解决操作 */@Overridepublic 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);
}
复制代码能够去反转树结构 用途的看本人的业务需要 能够看一下思路
总结兴许有很多类去实现树结构 只须要很少的代码 然而有些思路还是要学的 也有其余暴力组成 然而基本思路差不多 如果各位大佬有更好的思路 能够揭示一下 祝顺利