前言
List转Tree的两种方法,一种是使用对象的特性,方法极其巧妙,一种是使用Hutool的工具方法,对于复杂的结构可能更友好。
还要递归也能实现,不过网上好多方法,这里不做赘述,如需要递归方法实现请自行百度。
数据准备:一批菜单,结构如下:
public class AppMenu {
// 菜单id
private Integer ameId;
// 父菜单id
private Integer parentId;
// ..其它字段无需理会
}
方法一:使用Map实现
使用这个方法需要先声明好菜单的树形类,这里只加了子菜单列表的属性
如果需要排序,需要提前对列表进行排序
public class AppMenuTree extends AppMenuWithBLOBs {
List<AppMenuTree> child;
}
实现原理
-
先使用一个创建一个Map,根据父菜单id对所有菜单进行分类,此时Map的key为父菜单id,value为子菜单集合
-
遍历所有Map,此时Map的value为某一个菜单的所有子菜单,遍历这些子菜单,为这些子菜单赋予子菜单(难点,下文有举例说明以助理解)
-
返回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)
- 菜单2(id = 2,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字段传入,自动排序
-
方法一只是最原始的转换,方法二还有更多扩展功能
总结
个人比较推荐方法一,这个原理非常巧妙,方法二只是一个使用方法,如果不去跟踪原理,对自身的提升有限,如果两种方法都掌握,自然是最好的
方法二的拓展性较强,后面遇到比较复杂的结构,方法二更适合
???? 欢迎前往博客主页查看更多内容
???? 如果觉得不错,期待您的点赞、收藏、评论、关注
???? 如有错误欢迎指正!