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());
总结
- 方法一:
优点:逻辑简单清晰
缺点:代码不能复用 - 方法二:
优点:代码可以复用
缺点:需要实现接口,代码侵入性太强 - 方法三:
优点:无代码侵入,可复用
缺点:需要引用Hutool包,返回对象变成Hutoo的Tree - 方法四:
优点:无代码侵入,代码可复用,不需要引用额外包
缺点:不支持JDK1.8以下版本
以上是关于JAVA中生成菜单树结构常用方法总结的主要内容,如果未能解决你的问题,请参考以下文章
使用带有递归的reduce函数从多级树中生成一个扁平的id数组?