From 5374b0567be66364cb4c98b1ca1aa5f99ba98d89 Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Thu, 20 Nov 2025 15:09:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=81=E8=A3=85=E6=A0=91=E5=BD=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=93=8D=E4=BD=9C=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/tree/TreeUtil.java | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/tree/TreeUtil.java diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/tree/TreeUtil.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/tree/TreeUtil.java new file mode 100644 index 00000000..97a7ac73 --- /dev/null +++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/tree/TreeUtil.java @@ -0,0 +1,216 @@ +package com.zt.plat.framework.common.util.tree; + +import com.alibaba.fastjson.JSON; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import lombok.Data; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collectors; + +/** + * 树操作方法工具类 + */ +public class TreeUtil { + /** + * 将list合成树 + *@param list 需要合成树的List + *@param rootCheck 判断E中为根节点的条件,如:x->x.getPId()==-1L,x->x.getParentId()==null,x->x.getParentMenuId()==0 + *@param parentCheck 判断E中为父节点条件,如:(x,y)->x.getId().equals(y.getPId()) + *@param setSubChildren E中设置下级数据方法,如:Menu::setSubMenus + *@param 泛型实体对象 + *@return 合成好的树 + */ + public static List makeTree(List list, Predicate rootCheck, BiFunction parentCheck, BiConsumer> setSubChildren){ + return list.stream().filter(rootCheck).peek(x->setSubChildren.accept(x,makeChildren(x,list,parentCheck,setSubChildren))).collect(Collectors.toList()); + } + + /** + *将树打平成tree + *@paramtree需要打平的树 + *@paramgetSubChildren设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null) + *@paramsetSubChildren将下级数据置空方法,如:x->x.setSubMenus(null) + *@return打平后的数据 + *@param泛型实体对象 + */ + public static List flat(List tree, Function> getSubChildren, Consumer setSubChildren){ + List res = new ArrayList<>(); + forPostOrder(tree,item->{ + setSubChildren.accept(item); + res.add(item); + },getSubChildren); + return res; + } + + + /** + *前序遍历 + * + *@paramtree需要遍历的树 + *@paramconsumer遍历后对单个元素的处理方法,如:x->System.out.println(x)、System.out::println打印元素 + *@paramsetSubChildren设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null) + *@param泛型实体对象 + */ + public static void forPreOrder(List tree,Consumer consumer,Function> setSubChildren){ + for(E l : tree){ + consumer.accept(l); + List es = setSubChildren.apply(l); + if(es != null && es.size() > 0){ + forPreOrder(es,consumer,setSubChildren); + } + } + } + + + /** + *层序遍历 + * + *@paramtree需要遍历的树 + *@paramconsumer遍历后对单个元素的处理方法,如:x->System.out.println(x)、System.out::println打印元素 + *@paramsetSubChildren设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null) + *@param泛型实体对象 + */ + public static void forLevelOrder(List tree,Consumer consumer,Function> setSubChildren){ + Queue queue=new LinkedList<>(tree); + while(!queue.isEmpty()){ + E item = queue.poll(); + consumer.accept(item); + List childList = setSubChildren.apply(item); + if(childList !=null && !childList.isEmpty()){ + queue.addAll(childList); + } + } + } + + + /** + *后序遍历 + * + *@paramtree需要遍历的树 + *@paramconsumer遍历后对单个元素的处理方法,如:x->System.out.println(x)、System.out::println打印元素 + *@paramsetSubChildren设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null) + *@param泛型实体对象 + */ + public static void forPostOrder(List tree,Consumer consumer,Function> setSubChildren){ + for(E item : tree) { + List childList = setSubChildren.apply(item); + if(childList != null && !childList.isEmpty()){ + forPostOrder(childList,consumer,setSubChildren); + } + consumer.accept(item); + } + } + + /** + *对树所有子节点按comparator排序 + * + *@paramtree需要排序的树 + *@paramcomparator排序规则Comparator,如:Comparator.comparing(MenuVo::getRank)按Rank正序,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序 + *@paramgetChildren获取下级数据方法,如:MenuVo::getSubMenus + *@return排序好的树 + *@param泛型实体对象 + */ + public static List sort(List tree, Comparator comparator, Function> getChildren){ + for(E item : tree){ + List childList = getChildren.apply(item); + if(childList != null &&! childList.isEmpty()){ + sort(childList,comparator,getChildren); + } + } + tree.sort(comparator); + return tree; + } + + private static List makeChildren(E parent,List allData,BiFunction parentCheck,BiConsumer> children){ + return allData.stream().filter(x->parentCheck.apply(parent,x)).peek(x->children.accept(x,makeChildren(x,allData,parentCheck,children))).collect(Collectors.toList()); + } + + /** + * 使用样例 + * @param args + */ + public static void main(String[] args) { + MenuVo menu0 = new MenuVo(0L, -1L, "一级菜单", 0); + MenuVo menu1 = new MenuVo(1L, 0L, "二级菜单", 1); + MenuVo menu2 = new MenuVo(2L, 0L, "三级菜单", 2); + MenuVo menu3 = new MenuVo(3L, 1L, "四级菜单", 3); + MenuVo menu4 = new MenuVo(4L, 1L, "五级菜单", 4); + MenuVo menu5 = new MenuVo(5L, 2L, "六级菜单", 5); + MenuVo menu6 = new MenuVo(6L, 2L, "七级菜单", 6); + MenuVo menu7 = new MenuVo(7L, 3L, "八级菜单", 7); + MenuVo menu8 = new MenuVo(8L, 3L, "九级菜单", 8); + MenuVo menu9 = new MenuVo(9L, 4L, "十级菜单", 9); + //基本数据 + List menuList = Arrays.asList(menu0,menu1, menu2,menu3,menu4,menu5,menu6,menu7,menu8,menu9); + //合成树 + /** + * 第1个参数List list,为我们需要合成树的List,如上面Demo中的menuList + * 第2个参数Predicate rootCheck,判断为根节点的条件,如上面Demo中pId==-1就是根节点 + * 第3个参数parentCheck 判断为父节点条件,如上面Demo中 id==pId + * 第4个参数setSubChildren,设置下级数据方法如上面Demo中:Menu::setSubMenus + */ + List tree= TreeUtil.makeTree(menuList, x->x.getPId()==-1L,(x, y)->x.getId().equals(y.getPId()), MenuVo::setSubMenus); + System.out.println(JSON.toJSONString(tree)); + + //先序 + /** + * 遍历数参数解释: + * tree 需要遍历的树,就是makeTree()合 + * 成的对象Consumer consumer 遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 postOrder.append(x.getId().toString()) + * Function getSubChildren,获取下级数据方法,如Menu::getSubMenus + */ + StringBuffer preStr = new StringBuffer(); + TreeUtil.forPreOrder(tree,x-> preStr.append(x.getId().toString()),MenuVo::getSubMenus); + ObjectUtils.equalsAny("0123456789",preStr.toString()); + + //层序 + StringBuffer levelStr=new StringBuffer(); + TreeUtil.forLevelOrder(tree,x-> levelStr.append(x.getId().toString()),MenuVo::getSubMenus); + ObjectUtils.equalsAny("0123456789",levelStr.toString()); + + //后序 + StringBuffer postOrder=new StringBuffer(); + TreeUtil.forPostOrder(tree,x-> postOrder.append(x.getId().toString()),MenuVo::getSubMenus); + ObjectUtils.equalsAny("7839415620",postOrder.toString()); + + // 树平铺 + /** + * flat()参数解释: + * tree 需要打平的树,就是makeTree()合成的对象Function getSubChildren, + * 获取下级数据方法,如Menu::getSubMenusConsumer setSubChildren, + * 设置下级数据方法,如:x->x.setSubMenus(null) + */ + List flat = TreeUtil.flat(tree, MenuVo::getSubMenus,x->x.setSubMenus(null)); + ObjectUtils.equalsAny(flat.size(),menuList.size()); + flat.forEach(x -> { + if (x.getSubMenus() != null) { + throw new RuntimeException("树平铺失败"); + } + }); + + // 按rank正序 + /** + * sort参数解释: + * tree 需要排序的树,就是makeTree()合成的对象Comparator comparator + * 排序规则Comparator,如:Comparator.comparing(MenuVo::getRank) 按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序Function getChildren + * 获取下级数据方法,如:MenuVo::getSubMenus + */ + List sortTree= TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus); + // 按rank倒序 + List sortTreeReverse = TreeUtil.sort(tree, (x,y)->y.getRank().compareTo(x.getRank()), MenuVo::getSubMenus); + } + @Data + static class MenuVo { + private Long id; // 主键id + private Long pId; // 父级id + private String name; // 菜单名称 + private Integer rank = 0; // 排序 + private List subMenus = new ArrayList<>(); // 子菜单 + public MenuVo(Long id, Long pId, String name, Integer rank) { + this.id = id; + this.pId = pId; + this.name = name; + this.rank = rank; + } + } +}