List转Tree的两种方法(无递归 )

前言

List转Tree的两种方法,一种是使用对象的特性,方法极其巧妙,一种是使用Hutool的工具方法,对于复杂的结构可能更友好。

还要递归也能实现,不过网上好多方法,这里不做赘述,如需要递归方法实现请自行百度。

数据准备:一批菜单,结构如下:

public class AppMenu {
    // 菜单id
    private Integer ameId;
	// 父菜单id
    private Integer parentId;
    // ..其它字段无需理会
}

方法一:使用Map实现

使用这个方法需要先声明好菜单的树形类,这里只加了子菜单列表的属性

如果需要排序,需要提前对列表进行排序

public class AppMenuTree extends AppMenuWithBLOBs {
    List<AppMenuTree> child;
}

实现原理

  1. 先使用一个创建一个Map,根据父菜单id对所有菜单进行分类,此时Map的key为父菜单id,value为子菜单集合

  2. 遍历所有Map,此时Map的value为某一个菜单的所有子菜单,遍历这些子菜单,为这些子菜单赋予子菜单(难点,下文有举例说明以助理解

  3. 返回Key为顶级菜单id的列表(这里顶级菜单Id为-1)

实现代码

public List<AppMenuTree> listToTreeByMap() {
    // 获取菜单列表
    List<AppMenuTree> menuList = Convert.toList(AppMenuTree.class, getMenuList());
    // 根据父菜单id进行分类
    Map<Integer, List<AppMenuTree>> parentMap = menuList.stream()
        .collect(Collectors.groupingBy(AppMenuTree::getParentId));
    for (Entry<Integer, List<AppMenuTree>> entry : parentMap.entrySet()) {
        List<AppMenuTree> childMenus = entry.getValue();
        // 对子菜单赋值
        for (AppMenuTree childMenu : childMenus) {
            childMenu.setChild(parentMap.get(childMenu.getAmeId()));
        }
    }
    // 返回根菜单
    return parentMap.get(-1);
}

举例说明

对于第2点,这里非常之巧妙,也不点不好理解,这里举例说明一下

数据准备如下:

  • 菜单1(id = 1,parentId = -1)
    • 菜单2(id = 2,parentId = 1)
      • 菜单3(id = 3,parentId = 2)
      • 菜单4(id = 4,parentId = 2)
    • 菜单5(id = 5,parentId = 1)

根据父Id进行分类结果,Map的值如下

{
	{-1,[1]},
	{1,[2,5]},
	{2,[3,4]}
}

在遍历时,第一次将id为1的菜单的子菜单设置为2和5,此时菜单2和5的子菜单为空,第二次遍历将菜单2的子菜单设置为3和4,菜单5的子菜单为空,重点来了,这个时候,取出key为-1的菜单,它的值就是最终的结果,原因是菜单的child是引用型,第一次遍历的时候已经将菜单2的引用赋值给了菜单1,第二次遍历的时候改变菜单的属性,菜单1里的菜单2也会随着变化。

方法二:使用Hutool工具方法实现

附上Hutool官网链接可以在官网查看详细使用方法与其他工具方法

介绍TreeUtil相关工具链接

代码实现

public List<Tree<Integer>> listToTreeByHutool() {
    // 获取数据
    List<AppMenuWithBLOBs> menuList = getMenuList();
    // 构建node列表
    List<TreeNode<Integer>> nodeList = menuList.stream().map(menu -> {
        // 名称
        String name = getMenuFiled(menu.getBasicJson(), "name");
        // 排序
        Integer sort = 0;
        try {
            sort = Integer.valueOf(getMenuFiled(menu.getExpendJson(), "sort_number"));
        } catch (Exception e) {
            sort = 0;
        }
        // TreeNode构造方法: public TreeNode(T id, T parentId, String name, Comparable<?> weight)
        // 参数:id,父id,名称,权重(这里传的是sort)值越小越在前面
        TreeNode<Integer> treeNode = new TreeNode<Integer>(menu.getAmeId(), menu.getParentId(), name, sort);
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("appMenu", menu);
        // 拓展字段,类型为Map<String, Object>,这里将整个对象传进去
        treeNode.setExtra(hashMap);
        return treeNode;
    }).collect(Collectors.toList());
    // 构造树,将前面的node列表和菜单id传入
    return TreeUtil.build(nodeList, -1);
}

区别

  • 方法一需要自行声明Tree的类,方法二可以使用Hutool提供的Tree类

  • 方法一需要自己提前排序,方法二可以将sort字段传入,自动排序

  • 方法一只是最原始的转换,方法二还有更多扩展功能

总结

个人比较推荐方法一,这个原理非常巧妙,方法二只是一个使用方法,如果不去跟踪原理,对自身的提升有限,如果两种方法都掌握,自然是最好的

方法二的拓展性较强,后面遇到比较复杂的结构,方法二更适合


👍 欢迎前往博客主页查看更多内容

👍 如果觉得不错,期待您的点赞、收藏、评论、关注

👍 ​ 如有错误欢迎指正!

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值