封装树形数据操作工具类
This commit is contained in:
@@ -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<E> 泛型实体对象
|
||||
*@return 合成好的树
|
||||
*/
|
||||
public static <E> List<E> makeTree(List<E> list, Predicate<E> rootCheck, BiFunction<E,E,Boolean> parentCheck, BiConsumer<E,List<E>> 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<E>泛型实体对象
|
||||
*/
|
||||
public static <E> List<E> flat(List<E> tree, Function<E,List<E>> getSubChildren, Consumer<E> setSubChildren){
|
||||
List<E> 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<E>泛型实体对象
|
||||
*/
|
||||
public static <E> void forPreOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
|
||||
for(E l : tree){
|
||||
consumer.accept(l);
|
||||
List<E> 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<E>泛型实体对象
|
||||
*/
|
||||
public static <E> void forLevelOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
|
||||
Queue<E> queue=new LinkedList<>(tree);
|
||||
while(!queue.isEmpty()){
|
||||
E item = queue.poll();
|
||||
consumer.accept(item);
|
||||
List<E> 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<E>泛型实体对象
|
||||
*/
|
||||
public static <E> void forPostOrder(List<E> tree,Consumer<E> consumer,Function<E,List<E>> setSubChildren){
|
||||
for(E item : tree) {
|
||||
List<E> 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<E>泛型实体对象
|
||||
*/
|
||||
public static <E> List<E> sort(List<E> tree, Comparator<? super E> comparator, Function<E,List<E>> getChildren){
|
||||
for(E item : tree){
|
||||
List<E> childList = getChildren.apply(item);
|
||||
if(childList != null &&! childList.isEmpty()){
|
||||
sort(childList,comparator,getChildren);
|
||||
}
|
||||
}
|
||||
tree.sort(comparator);
|
||||
return tree;
|
||||
}
|
||||
|
||||
private static <E> List<E> makeChildren(E parent,List<E> allData,BiFunction<E,E,Boolean> parentCheck,BiConsumer<E,List<E>> 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<MenuVo> 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<MenuVo> 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<E, List> 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<E, List> getSubChildren,
|
||||
* 获取下级数据方法,如Menu::getSubMenusConsumer setSubChildren,
|
||||
* 设置下级数据方法,如:x->x.setSubMenus(null)
|
||||
*/
|
||||
List<MenuVo> 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<? super E> comparator
|
||||
* 排序规则Comparator,如:Comparator.comparing(MenuVo::getRank) 按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序Function<E, List> getChildren
|
||||
* 获取下级数据方法,如:MenuVo::getSubMenus
|
||||
*/
|
||||
List<MenuVo> sortTree= TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus);
|
||||
// 按rank倒序
|
||||
List<MenuVo> 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<MenuVo> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user