JAVA中生成菜单树结构常用方法总结

Posted 赵侠客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA中生成菜单树结构常用方法总结相关的知识,希望对你有一定的参考价值。

前言

项目开发中经常会遇到树形结构,如多级菜单、多级文件夹结构、多级分类结构、多级组织结构,这些结构都有个共同特点,就是一般存在数据库中是通过id和parentId保存父子级关系的,返回给前端需要合成一颗树,本文针对这类数据结构,总结出常用合成树的三种方法。

方法一:递归合成法

一般在会有一个Menu对象

@Data
public class Menu  
    public Menu(Integer id, Integer parentId, String name, Integer weight) 
        this.id = id;
        this.parentId = parentId;
        this.name = name;
        this.weight = weight;
    

    private Integer id;
    private Integer parentId;
    private String name;
    private Integer weight;
    private List<Menu> children;

    public static void main(String[] args)  
        List<Menu> list = new ArrayList<>();
        list.add(new Menu(1, 0, "用户管理", 2));
        list.add(new Menu(2, 0, "租户管理", 1));
        list.add(new Menu(3, 1, "添加用户", 3));
        list.add(new Menu(4, 1, "删除用户", 2));
        list.add(new Menu(5, 2, "添加租户", 1));
        list.add(new Menu(6, 2, "删除租户", 2));
        
        List<Menu> tree1 = buildTree(list);
        System.out.println(JSONUtil.toJsonStr(tree1));
	
	
    public static List<Menu> buildTree(List<Menu> list) 
        return list.stream()
                .filter(menu -> menu.getParentId() == 0)
                .peek(menu -> menu.setChildren(getChildrens(menu, list)))
                .sorted(Comparator.comparing(Menu::getWeight))
                .collect(Collectors.toList());
    
    public static List<Menu> getChildrens(Menu root, List<Menu> allMenus) 
        return allMenus.stream()
                .filter(menu -> Objects.equals(menu.getParentId(), root.getId()))
                .peek(menu -> menu.setChildren(getChildrens(menu, allMenus)))
                .sorted(Comparator.comparing(Menu::getWeight))
                .collect(Collectors.toList());
    

方法二:使用泛型通用合成法


public interface TreeNode<T, E> extends Comparable<E> 
    T getId();
    T getParentId();
    T getWeight();
    boolean isRoot();
    void setChildren(List<? extends TreeNode<T, E>> children);


@Data
public class Menu implements TreeNode<Integer, Menu> 
    public Menu(Integer id, Integer parentId, String name, Integer weight) 
        this.id = id;
        this.parentId = parentId;
        this.name = name;
        this.weight = weight;
    
    private Integer id;
    private Integer parentId;
    private String name;
    private Integer weight;
    private List<Menu> children;
    @Override
    public void setChildren(List menus) 
        this.children = menus;
    
    @Override
    public boolean isRoot() 
        return this.parentId == 0;
    
    @Override
    public int compareTo(Menu o) 
        return this.weight.compareTo(o.getWeight());
    


    public static void main(String[] args) 
        List<Menu> list = new ArrayList<>();
        list.add(new Menu(1, 0, "用户管理", 2));
        list.add(new Menu(2, 0, "租户管理", 1));
        list.add(new Menu(3, 1, "添加用户", 3));
        list.add(new Menu(4, 1, "删除用户", 2));
        list.add(new Menu(5, 2, "添加租户", 1));
        list.add(new Menu(6, 2, "删除租户", 2));
        List<Menu> tree2 = tree(list);
        System.out.println(JSONUtil.toJsonStr(tree2));
    
    public static <E extends TreeNode> List<E> getSubs(E parent, List<E> allData) 
        return allData.stream()
                .filter(x -> Objects.equals(x.getParentId(), parent.getId()))
                .peek(x -> x.setChildren(getSubs(x, allData)))
                .sorted()
                .collect(Collectors.toList());
    
    public static <E extends TreeNode> List<E> tree(List<E> allData) 
        return allData.stream()
                .filter(TreeNode::isRoot)
                .peek(x -> x.setChildren(getSubs(x, allData)))
                .sorted()
                .collect(Collectors.toList());
    

方法三:使用Hutool 工具TreeUtill合成法

    public static void main(String[] args) 
        List<Menu> list = new ArrayList<>();
        list.add(new Menu(1, 0, "用户管理", 2));
        list.add(new Menu(2, 0, "租户管理", 1));
        list.add(new Menu(3, 1, "添加用户", 3));
        list.add(new Menu(4, 1, "删除用户", 2));
        list.add(new Menu(5, 2, "添加租户", 1));
        list.add(new Menu(6, 2, "删除租户", 2));


        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        treeNodeConfig.setIdKey("id");
        List<Tree<Integer>> tree3 = TreeUtil.build(list, 0, treeNodeConfig, (node, tree) -> 
            tree.setId(node.getId());
            tree.setParentId(node.getParentId());
            tree.setName(node.getName());
            tree.setWeight(node.getWeight());
        );
        System.out.println(JSONUtil.toJsonStr(tree3));
    

方法四:使用Lambda 表达式改写泛型接口


    public static void main(String[] args) 
        List<Menu> list = new ArrayList<>();
        list.add(new Menu(1, 0, "用户管理", 2));
        list.add(new Menu(2, 0, "租户管理", 1));
        list.add(new Menu(3, 1, "添加用户", 3));
        list.add(new Menu(4, 1, "删除用户", 2));
        list.add(new Menu(5, 2, "添加租户", 1));
        list.add(new Menu(6, 2, "删除租户", 2));
        List<Menu> menus= makeTree(list,x-> x.getParentId()==0,(x,y)-> x.getId().equals(y.getParentId()), Menu::setChildren);
        System.out.println(JSONUtil.toJsonStr(menus));
    

    public static <E> List<E> makeTree(List<E> list, Predicate<E> root, BiFunction<E,E,Boolean> parentCheck, BiConsumer<E,List<E>> children) 
        return list.stream().filter(root).peek(x->children.accept(x,makeChildren(x,list, parentCheck,children))).collect(Collectors.toList());
    
    public 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. 方法一:
    优点:逻辑简单清晰
    缺点:代码不能复用
  2. 方法二:
    优点:代码可以复用
    缺点:需要实现接口,代码侵入性太强
  3. 方法三:
    优点:无代码侵入,可复用
    缺点:需要引用Hutool包,返回对象变成Hutoo的Tree
  4. 方法四:
    优点:无代码侵入,代码可复用,不需要引用额外包
    缺点:不支持JDK1.8以下版本

以上是关于JAVA中生成菜单树结构常用方法总结的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中生成菜单树结构常用方法总结

在 MySQL 中生成连续的参考编号

解决Java中生成二级菜单栏JSON数据格式

使用带有递归的reduce函数从多级树中生成一个扁平的id数组?

JAVA WEB项目中生成验证码及验证实例(附源码及目录结构)

学习笔记Java中生成对象的5中方法