构建树的三种方法


平时大概率我们会构建一些树形结果返回给前端,比如菜单结构、部门列表、文件结构等,我们一般想到的就是利用递归来循环构建;现在,就我个人解决的方法如下:
 1.原始递归
 2.利用Java 8 Stream流进行处理(原理还是递归)
 3.Stream流升级构建

场景构建

@Data
public class TreeSelect implements Serializable{
    /** 节点ID */
    private Long id;

    /** 节点名称 */
    private String label;

    /** 父ID */
    private Long parentId;

    /** 子节点 */
    private List<TreeSelect> children;
}

1、原始递归构建树

/**
 * 构建前端所需要树结构
 *
 * @param trees 列表
 * @return 树结构列表
 */
public static List<TreeSelect> buildDeptTree(List<TreeSelect> trees)
{
    List<TreeSelect> returnList = new ArrayList<TreeSelect>();
    List<Long> tempList = new ArrayList<Long>();
    for (TreeSelect dept : trees)
    {
        tempList.add(dept.getId());
    }
    for (Iterator<TreeSelect> iterator = trees.iterator(); iterator.hasNext();)
    {
        TreeSelect treeSelect = (TreeSelect) iterator.next();
        // 如果是顶级节点, 遍历该父节点的所有子节点
        if (!tempList.contains(treeSelect.getParentId()))
        {
            recursionFn(trees, treeSelect);
            returnList.add(treeSelect);
        }
    }
    if (returnList.isEmpty())
    {
        returnList = trees;
    }
    return returnList;
}
/**
 * 递归列表
 */
private static void recursionFn(List<TreeSelect> list, TreeSelect t)
{
    // 得到子节点列表
    List<TreeSelect> childList = getChildList(list, t);
    t.setChildren(childList);
    for (TreeSelect tChild : childList)
    {
        if (hasChild(list, tChild))
        {
            recursionFn(list, tChild);
        }
    }
}

/**
 * 得到子节点列表
 */
private static List<TreeSelect> getChildList(List<TreeSelect> list, TreeSelect t)
{
    List<TreeSelect> tlist = new ArrayList<TreeSelect>();
    for (TreeSelect n : list) {
        if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getId().longValue()) {
            tlist.add(n);
        }
    }
    return tlist;
}

/**
 * 判断是否有子节点
 */
private static boolean hasChild(List<TreeSelect> list, TreeSelect t)
{
    return getChildList(list, t).size() > 0;
}

2、Stream流处理

Stream流处理原理还是递归

public static List<TreeSelect> buildDeptTreeByStream(List<TreeSelect> trees){
        //获取parentId = 0的根节点
        List<TreeSelect> list = trees.stream().filter(item -> item.getParentId() == 0L).collect(Collectors.toList());
        //根据parentId进行分组
        Map<Long, List<TreeSelect>> map = trees.stream().collect(Collectors.groupingBy(TreeSelect::getParentId));
        recursionFnTree(list, map);
        return list;
    }
/**
 * 递归遍历节点
 * @param list
 * @param map
 */
public static void recursionFnTree(List<TreeSelect> list, Map<Long, List<TreeSelect>> map){
    for (TreeSelect treeSelect : list) {
        List<TreeSelect> childList = map.get(treeSelect.getId());
        treeSelect.setChildren(childList);
        if (null != childList && 0 < childList.size()){
            recursionFnTree(childList,map);
        }
    }
}

3、Stream流升级构建

//获取父节点
List<TreeSelect> collect = trees.stream().filter(m -> m.getParentId() == 0).peek(
    m -> m.setChildren(getChildrenList(m, trees))
).collect(Collectors.toList());
/**
 * 获取子节点列表
 * @param tree
 * @param list
 * @return
 */
public static List<TreeSelect> getChildrenList(TreeSelect tree, List<TreeSelect> list){
    List<TreeSelect> children = list.stream().filter(item -> Objects.equals(item.getParentId(), tree.getId())).peek(
            item -> item.setChildren(getChildrenList(item, list))
    ).collect(Collectors.toList());
    return children;
}

个人还是比较倾向用Stream流构建树形结构,节省代码量还通俗易懂!!!Stream在实际开发过程中,运用得体的话,既能节省代码量,还能提高效率,但是复杂的流式处理数据也会让代码看起来不易理解!


文章作者: wmg
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wmg !
  目录