searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Java 树操作遍历工具

2024-09-27 09:20:53
2
0

一、直接上代码

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * @Description: 树操作方法工具类
 * @Author: 公众号:赵侠客
 * @Copyright: Copyright (c) 赵侠客
 * @Date: 2024-07-22 10:42
 * @Version: 1.0
 */
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
     *
     * @param tree           需要打平的树
     * @param getSubChildren 设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null)
     * @param setSubChildren 将下级数据置空方法,如:x->x.setSubMenus(null)
     * @param <E>            泛型实体对象
     * @return 打平后的数据
     */
    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;
    }


    /**
     * 前序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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);
            }
        }
    }


    /**
     * 层序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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);
            }
        }
    }


    /**
     * 后序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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排序
     *
     * @param tree        需要排序的树
     * @param comparator  排序规则Comparator,如:Comparator.comparing(MenuVo::getRank)按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序
     * @param getChildren 获取下级数据方法,如:MenuVo::getSubMenus
     * @param <E>         泛型实体对象
     * @return 排序好的树
     */
    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());
    }

 

二、如何使用

1、数据准备

@Data
public class MenuVo {
    private Long id;
    private Long pId;
    private String name;
    private Integer rank = 0;
    private List<MenuVo> subMenus = new ArrayList<>();

    public MenuVo(Long id, Long pId, Integer rank) {
        this.id = id;
        this.pId = pId;
        this.rank = rank;
    }
}



//基本数据
        MenuVo menu0 = new MenuVo(0L, -1L, 1);
        MenuVo menu1 = new MenuVo(1L, 0L, 2);
        MenuVo menu2 = new MenuVo(2L, 0L, 3);
        MenuVo menu3 = new MenuVo(3L, 1L, 4);
        MenuVo menu4 = new MenuVo(4L, 1L, 5);
        MenuVo menu5 = new MenuVo(5L, 2L, 6);
        MenuVo menu6 = new MenuVo(6L, 2L, 7);
        MenuVo menu7 = new MenuVo(7L, 3L, 8);
        MenuVo menu8 = new MenuVo(8L, 3L, 9);
        MenuVo menu9 = new MenuVo(9L, 4L, 10);
        List<MenuVo> menuList = Arrays.asList(menu0, menu1, menu2, menu3, menu4, menu5, menu6, menu7, menu8, menu9);

2、构建树

 //makeTree()构建树
        List<MenuVo> tree = TreeUtil.makeTree(menuList, x -> x.getPId() == -1L, (x, y) -> x.getId().equals(y.getPId()), MenuVo::setSubMenus);
        System.out.println(JsonUtils.toJson(tree));

3、遍历Tree

 //先序
        StringBuffer preStr = new StringBuffer();
        TreeUtil.forPreOrder(tree, x -> preStr.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("先序=" + preStr);

        //层序
        StringBuffer levelStr = new StringBuffer();
        TreeUtil.forLevelOrder(tree, x -> levelStr.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("层序=" + levelStr);

        //后序
        StringBuffer postOrder = new StringBuffer();
        TreeUtil.forPostOrder(tree, x -> postOrder.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("后序=" + postOrder);

4、sort()排查

 //rank正序:
        List<MenuVo> sortTreeAsc = TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus);
        for (MenuVo menuVo : sortTreeAsc) {
            System.out.println("rank正序:" + menuVo);
        }
        //rank倒序:
        List<MenuVo> sortTreeDesc = TreeUtil.sort(tree, (x, y) -> y.getRank().compareTo(x.getRank()), MenuVo::getSubMenus);
        for (MenuVo menuVo : sortTreeDesc) {
            System.out.println("rank倒序:" + menuVo);
        }

5、flat打平树

注意:flat后,tree不再有树形结构

 List<MenuVo> flat = TreeUtil.flat(tree, MenuVo::getSubMenus, x -> x.setSubMenus(null));
        for (MenuVo menuVo : flat) {
            System.out.println("flat打平树=" + menuVo);
        }

 

0条评论
作者已关闭评论
孟****寅
44文章数
0粉丝数
孟****寅
44 文章 | 0 粉丝
原创

Java 树操作遍历工具

2024-09-27 09:20:53
2
0

一、直接上代码

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * @Description: 树操作方法工具类
 * @Author: 公众号:赵侠客
 * @Copyright: Copyright (c) 赵侠客
 * @Date: 2024-07-22 10:42
 * @Version: 1.0
 */
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
     *
     * @param tree           需要打平的树
     * @param getSubChildren 设置下级数据方法,如:Menu::getSubMenus,x->x.setSubMenus(null)
     * @param setSubChildren 将下级数据置空方法,如:x->x.setSubMenus(null)
     * @param <E>            泛型实体对象
     * @return 打平后的数据
     */
    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;
    }


    /**
     * 前序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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);
            }
        }
    }


    /**
     * 层序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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);
            }
        }
    }


    /**
     * 后序遍历
     *
     * @param tree           需要遍历的树
     * @param consumer       遍历后对单个元素的处理方法,如:x-> System.out.println(x)、 System.out::println打印元素
     * @param setSubChildren 设置下级数据方法,如: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排序
     *
     * @param tree        需要排序的树
     * @param comparator  排序规则Comparator,如:Comparator.comparing(MenuVo::getRank)按Rank正序 ,(x,y)->y.getRank().compareTo(x.getRank()),按Rank倒序
     * @param getChildren 获取下级数据方法,如:MenuVo::getSubMenus
     * @param <E>         泛型实体对象
     * @return 排序好的树
     */
    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());
    }

 

二、如何使用

1、数据准备

@Data
public class MenuVo {
    private Long id;
    private Long pId;
    private String name;
    private Integer rank = 0;
    private List<MenuVo> subMenus = new ArrayList<>();

    public MenuVo(Long id, Long pId, Integer rank) {
        this.id = id;
        this.pId = pId;
        this.rank = rank;
    }
}



//基本数据
        MenuVo menu0 = new MenuVo(0L, -1L, 1);
        MenuVo menu1 = new MenuVo(1L, 0L, 2);
        MenuVo menu2 = new MenuVo(2L, 0L, 3);
        MenuVo menu3 = new MenuVo(3L, 1L, 4);
        MenuVo menu4 = new MenuVo(4L, 1L, 5);
        MenuVo menu5 = new MenuVo(5L, 2L, 6);
        MenuVo menu6 = new MenuVo(6L, 2L, 7);
        MenuVo menu7 = new MenuVo(7L, 3L, 8);
        MenuVo menu8 = new MenuVo(8L, 3L, 9);
        MenuVo menu9 = new MenuVo(9L, 4L, 10);
        List<MenuVo> menuList = Arrays.asList(menu0, menu1, menu2, menu3, menu4, menu5, menu6, menu7, menu8, menu9);

2、构建树

 //makeTree()构建树
        List<MenuVo> tree = TreeUtil.makeTree(menuList, x -> x.getPId() == -1L, (x, y) -> x.getId().equals(y.getPId()), MenuVo::setSubMenus);
        System.out.println(JsonUtils.toJson(tree));

3、遍历Tree

 //先序
        StringBuffer preStr = new StringBuffer();
        TreeUtil.forPreOrder(tree, x -> preStr.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("先序=" + preStr);

        //层序
        StringBuffer levelStr = new StringBuffer();
        TreeUtil.forLevelOrder(tree, x -> levelStr.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("层序=" + levelStr);

        //后序
        StringBuffer postOrder = new StringBuffer();
        TreeUtil.forPostOrder(tree, x -> postOrder.append(x.getId().toString()), MenuVo::getSubMenus);
        System.out.println("后序=" + postOrder);

4、sort()排查

 //rank正序:
        List<MenuVo> sortTreeAsc = TreeUtil.sort(tree, Comparator.comparing(MenuVo::getRank), MenuVo::getSubMenus);
        for (MenuVo menuVo : sortTreeAsc) {
            System.out.println("rank正序:" + menuVo);
        }
        //rank倒序:
        List<MenuVo> sortTreeDesc = TreeUtil.sort(tree, (x, y) -> y.getRank().compareTo(x.getRank()), MenuVo::getSubMenus);
        for (MenuVo menuVo : sortTreeDesc) {
            System.out.println("rank倒序:" + menuVo);
        }

5、flat打平树

注意:flat后,tree不再有树形结构

 List<MenuVo> flat = TreeUtil.flat(tree, MenuVo::getSubMenus, x -> x.setSubMenus(null));
        for (MenuVo menuVo : flat) {
            System.out.println("flat打平树=" + menuVo);
        }

 

文章来自个人专栏
行业动态-mdy
44 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0