递归遍历菜单树(java8)

该代码示例展示了如何在Java中使用Lombok注解创建一个Menu实体类,用于表示树形结构的数据。程序实现了遍历多个根节点的树结构并构建子节点,以及获取从根节点到任意节点的路径功能。主要方法包括getTree()用于生成树结构,getPath()用于计算节点路径。

实体类

package org.example;

import lombok.Data;

import java.util.List;

@Data
public class Menu {
    /**
     * id
     */
    private String id;

    /**
     * 菜单名称
     */
    private String name;

    /**
     * 父节点id
     */
    private String pid;

    /**
     * 子节点
     */
    private List<Menu> children;

    /**
     * 带参构造
     */
    public Menu(String id, String name, String pid) {
        this.id = id;
        this.name = name;
        this.pid = pid;
    }
}

1.遍历属性结构(多个根节点)

package org.example;


import com.alibaba.fastjson2.JSONObject;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class App {
    public static void main(String[] args) {
        //模拟数据库查出所有数据(多个根节点)
        List<Menu> menus = Arrays.asList(
                new Menu("1", "根节点1", "0"),
                new Menu("2", "子节点1-1", "1"),
                new Menu("3", "子子节点1-1-1", "2"),
                new Menu("4", "子子子节点1-1-1-1", "3"),
                new Menu("5", "子节点1-2", "1"),
                new Menu("6", "子子节点1-2-1", "5"),
                new Menu("7", "根节点2", "0"),
                new Menu("8", "子节点2-1", "7"),
                new Menu("9", "子子节点2-1-1", "8")
        );
        List<Menu> tree = getTree(menus);
        System.out.println(JSONObject.toJSONString(tree));
    }

    /**
     * 遍历树(多个根节点)
     *
     * @param menus
     */
    public static List<Menu> getTree(List<Menu> menus) {
        // 筛选出根节点
        return menus.stream().filter(m -> Objects.equals(m.getPid(), "0")).map(m ->
        {
            //设置每个树的子节点
            m.setChildren(getChildrens(m, menus));
            return m;
        }).collect(Collectors.toList());
    }

    /**
     * 获取子节点
     *
     * @param root
     * @param menus
     * @return
     */
    public static List<Menu> getChildrens(Menu root, List<Menu> menus) {
        //筛选直接子节点
        return menus.stream().filter(m -> Objects.equals(m.getPid(), root.getId())).map(
                m -> {
                    // 递归设置子节点
                    m.setChildren(getChildrens(m, menus));
                    return m;
                }
        ).collect(Collectors.toList());
    }
}

输出:

[{"children":[{"children":[{"children":[{"children":[],"id":"4","name":"子子子节点1-1-1-1","pid":"3"}],"id":"3","name":"子子节点1-1-1","pid":"2"}],"id":"2","name":"子节点1-1","pid":"1"},{"children":[{"children":[],"id":"6","name":"子子节点1-2-1","pid":"5"}],"id":"5","name":"子节点1-2","pid":"1"}],"id":"1","name":"根节点1","pid":"0"},{"children":[{"children":[{"children":[],"id":"9","name":"子子节点2-1-1","pid":"8"}],"id":"8","name":"子节点2-1","pid":"7"}],"id":"7","name":"根节点2","pid":"0"}]

2.获取节点路径path

一般新增节点时将节点路径存入数据库,便于查看从根节点到子节点的路径

    /**
     * 获取节点路径
     *
     * @param id  要计算的节点id
     * @param map
     * @return
     */
    public static String getPath(String id, Map<String, String> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        List<String> pathList = new ArrayList<>();
        // 构建路径
        buildPath(id, map, pathList);
        // 翻转
        Collections.reverse(pathList);
        return StringUtils.join(pathList, ",");
    }

    /**
     * 构建路径
     *
     * @param id       节点id
     * @param map
     * @param pathList 路径
     */
    public static void buildPath(String id, Map<String, String> map, List<String> pathList) {
        if (Objects.isNull(id) || Objects.equals(id, "0")) {
            return;
        }
        pathList.add(id);
        String pid = map.get(id);
        buildPath(pid, map, pathList);
    }

测试:

    public static void main(String[] args) {
        //模拟数据库查出所有数据(多个根节点)
        List<Menu> menus = Arrays.asList(
                new Menu("1", "根节点1", "0"),
                new Menu("2", "子节点1-1", "1"),
                new Menu("3", "子子节点1-1-1", "2"),
                new Menu("4", "子子子节点1-1-1-1", "3"),
                new Menu("5", "子节点1-2", "1"),
                new Menu("6", "子子节点1-2-1", "5"),
                new Menu("7", "根节点2", "0"),
                new Menu("8", "子节点2-1", "7"),
                new Menu("9", "子子节点2-1-1", "8")
        );
        // 模拟从数据库查询出父子关系
        Map<String, String> map = menus.stream().collect(Collectors.toMap(Menu::getId,
                Menu::getPid));
        System.out.println(getPath("1", map));  // 1
        System.out.println(getPath("3", map));  // 1,2,3
        System.out.println(getPath("9", map));  // 7,8,9
    }

/** * 根据等级查询类目树 * * @param level * @return */ @Override public List queryCategoryTree(Integer level) { //查询当前级别下类目 List list = categoryDAO.list(level); //组装好的类目树,返回前端 List categoryTree = new ArrayList(); //所有类目 List allDTOList = new ArrayList(); if (CollectionUtils.isEmpty(list)) { return categoryTree; } for (CategoryDO categoryDO : list) { allDTOList.add(new CategoryTreeDTO().convertDOToDTO(categoryDO)); } //当前等级类目 categoryTree = allDTOList.stream().filter(dto -> level.equals(dto.getLevel())).collect(Collectors.toList()); for (CategoryTreeDTO categoryTreeDTO : categoryTree) { //组装类目为树结构 assembleTree(categoryTreeDTO, allDTOList,Constants.CATEGORY_MAX_LEVEL - level); } return categoryTree; } /** * 组装树 * * @param categoryTreeDTO * @param allList * @param remainRecursionCount 剩余递归次数 * @return */ public CategoryTreeDTO assembleTree(CategoryTreeDTO categoryTreeDTO, List allList, int remainRecursionCount) { remainRecursionCount--; //最大递归次数不超过Constants.CATEGORY_MAX_LEVEL-level次,防止坏数据死循环 if(remainRecursionCount < 0){ return categoryTreeDTO; } String categoryCode = categoryTreeDTO.getCategoryCode(); Integer level = categoryTreeDTO.getLevel(); //到达最后等级树返回 if (Constants.CATEGORY_MAX_LEVEL == level) { return categoryTreeDTO; } //子类目 List child = allList.stream().filter(a -> categoryCode.equals(a.getParentCode())).collect(Collectors.toList()); if (null == child) { return categoryTreeDTO; } categoryTreeDTO.setChildren(child); //组装子类目 for (CategoryTreeDTO dto : child) { assembleTree(dto, allList,remainRecursionCount); } return categoryTreeDTO; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值