Java中将list转为树形结构

2022-07-28 08:45:41

在系统开发过程中,可能会碰到一些需求,需要构建树形结构,数据库一般就使用父id来表示,比如构建菜单、构建地区级联、构建部门层级结构等等。虽然可以通过数据库SQL查询,但我们一般都是通过SQL一次性查询出所有数据,在程序中处理成树形结构。本文讲述如何将一个List<T> 处理成想要的树形结构。
1、自己测试过没问题的:

/**
     * 对象List转为Tree树形结构
     *
     * @param entityList       传进来的泛型List
     * @param primaryFieldName 主键名称
     * @param parentFieldName  父级字段名称
     * @return
     */
    public final List<Map<String, Object>> listToTree(List<Map<String, Object>> entityList, String primaryFieldName, String parentFieldName) {
        //返回的map Tree树形结构
        List<Map<String, Object>> treeMap = new ArrayList<>();
        //将传进的参数entityList转为MapList
        List<Map<String, Object>> listMap = JSON.parseObject(JSON.toJSONString(entityList), List.class);
        //声明一个map用来存listMap中的对象,key为对象id,value为对象本身
        Map<String, Map<String, Object>> entityMap = new Hashtable<>();
        //循环listMap把map对象put到entityMap中去
        listMap.forEach(map -> entityMap.put(map.get(primaryFieldName).toString(), map));
        //循环listMap进行Tree树形结构组装
        listMap.forEach(map -> {
            //获取map的pid
            Object pid = map.get(parentFieldName);
            //判断pid是否为空或者为0,为空说明是最顶级,直接add到返回的treeMap中去
            if (pid == null || StringUtils.equals(pid.toString(), "0")) {
                treeMap.add(map);
            } else {
                //如果pid不为空也不为0,是子集
                // 根据当前map的pid获取上级 parentMap
                Map<String, Object> parentMap = entityMap.get(pid.toString());
                if (parentMap == null) { //如果parentMap为空,则说明当前map没有父级,当前map就是顶级
                    treeMap.add(map);
                } else {
                    //如果parentMap不为空,则当前map为parentMap的子级
                    //取出parentMap的所有子级的List集合
                    List<Map<String, Object>> children = (List<Map<String, Object>>) parentMap.get("children");
                    if (children == null) {  //判断子级集合是否为空,为空则新创建List
                        children = new ArrayList<>();
                        parentMap.put("children", children);
                    }
                    //把当前map对象add到parentMap的子级List中去
                    children.add(map);
                    /**
                     * 因为parentMap是从entityMap中get出来的,
                     * 而entityMap中的value又是来自于listMap对象,
                     * 所以parentMap和entityMap中的value的地址都是指向listMap中的对象,
                     * 所以parentMap的children和entityMap中的value的children改变时,都会改变listMap中的对象,
                     * 这里涉及到了地址、指针,就不多说了。
                     */
                }
            }
        });
        return treeMap;
    }

2、for 方法转树形

public class TreeTest {
    public static void main(String[] args) {
        List<Tree> node = forMethod(treeList);
        System.out.println(node);
    }

    /**
     * 双重for循环方法转换成树形结构
     * @param treeList
     * @return
     */
    public static List<Tree> forMethod(List<Tree> treeList) {
        List<Tree> rootTree = new ArrayList<>();
        for (Tree tree : treeList) {
            // 第一步 筛选出最顶级的父节点
            if (0 == tree.getParentId()) {
                rootTree.add(tree);
            }
            // 第二步 筛选出该父节点下的所有子节点列表 
            for (Tree node : treeList) {
                if (node.getParentId().equals(tree.getId())) {
                    if (CollectionUtils.isEmpty(tree.getChildren())) {
                        tree.setChildren(new ArrayList<>());
                    }
                    tree.getChildren().add(node);
                }
            }
        }
        return rootTree;
    }
}

3、递归方法转树形

public class TreeTest {
    public static void main(String[] args) {
        List<Tree> node = recursionMethod(treeList);
        System.out.println(node);
    }
  
    /**
     * 递归方法转换成树形结构
     * @param treeList
     * @return
     */
    public static List<Tree> recursionMethod(List<Tree> treeList) {
        List<Tree> trees = new ArrayList<>();
        for (Tree tree : treeList) {
            // 找出父节点
            if (0 == tree.getParentId()) {
                // 调用递归方法填充子节点列表
                trees.add(findChildren(tree, treeList));
            }
        }
        return trees;
    }

    /**
     * 递归方法
     * @param tree 父节点对象
     * @param treeList 所有的List
     * @return
     */
    public static Tree findChildren(Tree tree, List<Tree> treeList) {
        for (Tree node : treeList) {
            if (tree.getId().equals(node.getParentId())) {
                if (tree.getChildren() == null) {
                    tree.setChildren(new ArrayList<>());
                }
                // 递归 调用自身
                tree.getChildren().add(findChildren(node, treeList));
            }
        }
        return tree;
    }
}

 4、stream方法转树形

public class TreeTest {
    public static void main(String[] args) {
        List<Tree> node = recursionMethod(treeList);
        System.out.println(node);
    }
  
    /**
     * stream方法转换成树形结构
     * @param treeList
     * @return
     */
    public static List<Tree> streamMethod(List<Tree> treeList) {
        List<Tree> list = treeList.stream()
                                  // 筛选出父节点
                                  .filter(t -> t.getParentId() == 0)
                                  // 设置父节点的子节点列表
                                  .map(item -> {item.setChildren(streamGetChildren(item, treeList)); return item;})
                                  .collect(Collectors.toList());
        return list;
    }

    /**
     * stream 方式递归查找子节点列表
     * @return
     */
    public static List<Tree> streamGetChildren(Tree tree, List<Tree> treeList) {
        List<Tree> list = treeList.stream()
                                  .filter(t -> t.getParentId().equals(tree.getId()))
                                  .map(item -> {item.setChildren(streamGetChildren(item, treeList)); return item;})
                                  .collect(Collectors.toList());
        return list;
    }
}

 5、stream 转树形优化

// 第一种优化,我们合并上述两个方法的相同部分
public class TreeTest {
    public static void main(String[] args) {
        List<Tree> node = streamMethod(0, treeList);
        System.out.println(node);
    }

    /**
     * stream 方法转换树形结构方法的优化
     * @param parentId
     * @param treeList
     * @return
     */
    public static List<Tree> streamMethod(Integer parentId, List<Tree> treeList) {
        List<Tree> list = treeList.stream()
                // 筛选父节点
                .filter(t -> t.getParentId().equals(parentId))
                // 递归设置子节点
                .map(item -> {
                    item.setChildren(streamMethod(item.getId(), treeList));
                    return item;
                })
                .collect(Collectors.toList());
        return list;
    }
}

// 第二种优化,只是写法的不同,核心思路不变
public class TreeTest {
    public static void main(String[] args) {
        List<Tree> node = streamMethod(0, treeList);
        System.out.println(node);
    }
  
    /**
     * stream 方法转换树形结构方法的优化
     * @param parentId
     * @param treeList
     * @return
     */
    public static List<Tree> streamMethod(Integer parentId, List<Tree> treeList) {
        List<Tree> list = new ArrayList<>();
        Optional.ofNullable(treeList).orElse(new ArrayList<>())
                .stream()
                // 第一次筛选出主父节点列表进入循环,循环里面 进入递归 筛选出递归传递的从父节点列表
                .filter(root -> root.getParentId().equals(parentId))
                // 递归,最末的父节点从整个列表筛选出它的子节点列表依次组装
                .forEach(tree -> {
                    List<Tree> children = streamMethod(tree.getId(), treeList);
                    tree.setChildren(children);
                    list.add(tree);
                });
        return list;
    }
}

经过上面的代码,我们已经成功的将 List<T> 转换成了我们想要的树形结构了,但是此时有需求,要求我们文件夹名称后面附带文件夹包含的文件数量,

数据库或者构造的集合数据中的文件数量只统计了文件夹本身下面的文件数量,并没有加上子文件夹里面的所有文件夹数量。我们需要将子文件夹的 count 属性逐

级累加到父节点的 count 属性中更新它。

public class TreeTest {
    public static void main(String[] args) {
        // treeList 是已经处理好的 树形结构 集合
        countHandler(treeList);
        System.out.println(treeList);
    }

    /**
     * 递归将子节点属性值累加给父节点
     * @param treeList
     * @return
     */
    private int countHandler(List<Tree> treeList) {
        int count = 0;
        if(CollectionUtil.isEmpty(treeList)){
            return count;
        }
        for (Tree tree : treeList) {
            count += tree.getCount();
            if (CollectionUtil.isEmpty(tree.getChildren())) {
                continue;
            }
            count += countHandler(tree.getChildren());
            tree.setCount(count);
            if (tree.getParentId() == null || tree.getParentId() == 0) {
                count = 0;
            }
        }
        return count;
    }
}
  • 作者:zlfjavahome
  • 原文链接:https://blog.csdn.net/zlfjavahome/article/details/124960857
    更新时间:2022-07-28 08:45:41