java list集合转多叉树结构工具类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangpan_soft/article/details/82887827
  • 因为项目需求,修改将如下数据格式的数据转为树结构数据:
id parent
1 null
2 null
3 null
4 1
5 1
6 1
7 2
8 2
9 3
10 4
11 7
12 5
13 10
14 8
15 11
16 12
17 13
  • 将这样拥有父子依赖关系的数据转化为多叉树,本来想在网上找个现成代码的,百度半天,发现,说的云的雾的,思路感觉都很混乱,因此决定自己动手造轮子,因为我是管理整个项目架构的,所以这时候如果这个地方用,写一个,呢个地方用再写一个,明显不太科学,因此,我做了一个工具类,此工具类目前只提供2种功能,其一就是将目标集合转成多叉树集合,其二就是给目标节点添加孩子,至于其他的功能,因为项目中用不到,没有进行封装,后续如果有小伙伴需要用到多叉树的遍历,查找,排序,插入,删除功能的话再进行封装,大家需要用到这些功能可以给我留言,我在闲暇时间可能给你们封装一套
  • 好了废话少说,先上代码,代码中注释非常清晰,几乎逐行注释,所以在这里不多做代码的解释
package com.rjhcsoft.credit.utils;

import com.alibaba.fastjson.JSON;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;

public class TreeUtils {

    /**
     * 集合转树结构
     * @param collection 目标集合
     * @param clazz 集合元素类型
     * @param <T>
     * @return
     */
    public static <T> Collection<T> toTree(@NotNull Collection<T> collection,@NotNull Class<T> clazz){
        return toTree(collection,null,null,null,clazz);
    }

    /**
     * 集合转树结构
     * @param collection 目标集合
     * @param id 被依赖字段名称
     * @param parent 依赖字段名称
     * @param children 子节点集合属性名称
     * @param clazz 集合元素类型
     * @param <T>
     * @return
     */
    public static  <T> Collection<T> toTree(@NotNull Collection<T> collection,String id,String parent,String children,@NotNull Class<T> clazz){
        try {
            if (collection==null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
            if (StringUtil.isEmpty(id)) id = "id";// 如果被依赖字段名称为空则默认为id
            if (StringUtil.isEmpty(parent)) parent = "parent";// 如果依赖字段为空则默认为parent
            if (StringUtil.isEmpty(children)) children = "children";// 如果子节点集合属性名称为空则默认为children
            Collection<T> roots = null;// 初始化根节点集合
            if (collection.getClass().isAssignableFrom(Set.class)) roots = new HashSet<>();// 如果目标节点是一个set集合,则初始化根节点集合为hashset
            else roots = new ArrayList<>();// 否则初始化为Arraylist,
            // 这里集合初始化只分2中,要么是hashset,要么ArrayList,因为这两种最常用,其他不常用的摒弃
            Field idField = clazz.getDeclaredField(id);// 获取依赖字段
            Field parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            Field childrenField = clazz.getDeclaredField(children);// 获取孩子字段
            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);
            // 找出所有的根节点
            for (T c:collection){
                Object o = parentField.get(c);
                if (o instanceof String){
                    if (StringUtil.isEmpty((String) o)) {// 如果父节点为空则说明是根节点,添加到根节点集合
                        roots.add(c);
                    }
                }else {
                    if (o==null){
                        roots.add(c);
                    }
                }
            }
            // 从目标集合移除所有根节点
            collection.removeAll(roots);
            for (T c:roots){// 遍历根节点,依次添加子节点
                addChild(c,collection,idField,parentField,childrenField);
            }
            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);
            return roots;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子节点,此方法为私有,不能为公开,否则类修改信息无法恢复,后面有公开方法,其专门为目标节点添加子节点
     * @param c 目标节点
     * @param collection 目标集合
     * @param idField
     * @param parentField
     * @param childrenField
     * @param <T>
     * @throws IllegalAccessException
     */
    private static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,@NotNull Field idField,@NotNull Field parentField,@NotNull Field childrenField) throws IllegalAccessException {
        Object id =  idField.get(c);// 获取目标节点的被依赖值
        Collection<T> children = (Collection<T>) childrenField.get(c);// 获取目标节点的孩子列表
        for (T cc:collection){// 遍历目标集合
            Object o = parentField.get(cc);// 获取当前节点的依赖值
            if (id.equals(o)){// 如果当前节点的被依赖值和目标节点的被依赖值相等,则说明,当前节点是目标节点的子节点
                if (children==null) {// 如果目标节点的孩子集合为null,初始化目标节点的孩子集合
                    if (collection.getClass().isAssignableFrom(Set.class)){// 如果目标集合是一个set集合,则初始化目标节点的孩子节点集合为set
                        children = new HashSet<>();
                    }else children = new ArrayList<>();// 否则初始化为list
                }
                // 将当前节点添加到目标节点的孩子节点
                children.add(cc);
                // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
                childrenField.set(c,children);
                // 递归添加孩子节点
                addChild(cc,collection,idField,parentField,childrenField);
            }
        }
        // 特别说明:大家可以看到此递归没有明显出口,其出口就是是否当前节点的依赖值和目标节点的被依赖值一样,一样就递归,不一样进不了if,自然出递归
        // 此工具类自我感觉是最简单的,最实用的工具类,我看网上许多人写的,都是云的雾的,本来也想借鉴,但是实在没一个能看的感觉思路清晰,没办法,自己动手造轮子
    }

    /**
     * 为目标节点添加孩子
     * @param c 目标节点
     * @param collection 目标集合
     * @param id 被依赖字段名
     * @param parent 依赖字段名
     * @param children 孩子节点字段名
     * @param clazz 集合元素所在类别
     * @param <T>
     */
    public static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,String id,String parent,String children,@NotNull Class<T> clazz){
        try {
            if (collection==null || collection.isEmpty()) return ;// 如果目标集合为空,直接返回一个空树
            if (StringUtil.isEmpty(id)) id = "id";// 如果被依赖字段名称为空则默认为id
            if (StringUtil.isEmpty(parent)) parent = "parent";// 如果依赖字段为空则默认为parent
            if (StringUtil.isEmpty(children)) children = "children";// 如果子节点集合属性名称为空则默认为children
            Field idField = clazz.getDeclaredField(id);// 获取依赖字段
            Field parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            Field childrenField = clazz.getDeclaredField(children);// 获取孩子字段
            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);
            addChild(c,collection,idField,parentField,childrenField);
            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子
     * @param c 目标节点
     * @param collection 目标集合
     * @param clazz 集合元素所在类型
     * @param <T>
     */
    public static <T> void addChild(@NotNull T c,@NotNull Collection<T> collection,@NotNull Class<T> clazz){
        addChild(c,collection,null,null,null,clazz);
    }

    public static void main(String[] args){
        List<Menu> list = new ArrayList<>();
        list.add(new Menu(1,null));
        list.add(new Menu(2,null));
        list.add(new Menu(3,null));
        list.add(new Menu(4,1));
        list.add(new Menu(5,1));
        list.add(new Menu(6,1));
        list.add(new Menu(7,2));
        list.add(new Menu(8,2));
        list.add(new Menu(9,3));
        list.add(new Menu(10,4));
        list.add(new Menu(11,7));
        list.add(new Menu(12,5));
        list.add(new Menu(13,10));
        list.add(new Menu(14,8));
        list.add(new Menu(15,11));
        list.add(new Menu(16,12));
        list.add(new Menu(17,13));
        Collection<Menu> menus = TreeUtils.toTree(list, null, null, null, Menu.class);
        System.out.println(JSON.toJSONString(menus));
    }
}

class Menu{
    private Integer id;
    private Integer parent;
    private List<Menu> children;

    public Menu() {
    }

    public Menu(Integer id, Integer parent) {
        this.id = id;
        this.parent = parent;
    }

    public List<Menu> getChildren() {
        return children;
    }

    public void setChildren(List<Menu> children) {
        this.children = children;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getParent() {
        return parent;
    }

    public void setParent(Integer parent) {
        this.parent = parent;
    }
}


  • 最终打印的json数据如下

格式化之前

[{"children":[{"children":[{"children":[{"children":[{"id":"17","parent":"13"}],"id":"13","parent":"10"}],"id":"10","parent":"4"}],"id":"4","parent":"1"},{"children":[{"children":[{"id":"16","parent":"12"}],"id":"12","parent":"5"}],"id":"5","parent":"1"},{"id":"6","parent":"1"}],"id":"1"},{"children":[{"children":[{"children":[{"id":"15","parent":"11"}],"id":"11","parent":"7"}],"id":"7","parent":"2"},{"children":[{"id":"14","parent":"8"}],"id":"8","parent":"2"}],"id":"2"},{"children":[{"id":"9","parent":"3"}],"id":"3"}]

格式化之后

[
    {
        "children": [
            {
                "children": [
                    {
                        "children": [
                            {
                                "children": [
                                    {
                                        "children": [],
                                        "id": "17",
                                        "parent": "13"
                                    }
                                ],
                                "id": "13",
                                "parent": "10"
                            }
                        ],
                        "id": "10",
                        "parent": "4"
                    }
                ],
                "id": "4",
                "parent": "1"
            },
            {
                "children": [
                    {
                        "children": [
                            {
                                "children": [],
                                "id": "16",
                                "parent": "12"
                            }
                        ],
                        "id": "12",
                        "parent": "5"
                    }
                ],
                "id": "5",
                "parent": "1"
            },
            {
                "children": [],
                "id": "6",
                "parent": "1"
            }
        ],
        "id": "1"
    },
    {
        "children": [
            {
                "children": [
                    {
                        "children": [
                            {
                                "children": [],
                                "id": "15",
                                "parent": "11"
                            }
                        ],
                        "id": "11",
                        "parent": "7"
                    }
                ],
                "id": "7",
                "parent": "2"
            },
            {
                "children": [
                    {
                        "children": [],
                        "id": "14",
                        "parent": "8"
                    }
                ],
                "id": "8",
                "parent": "2"
            }
        ],
        "id": "2"
    },
    {
        "children": [
            {
                "children": [],
                "id": "9",
                "parent": "3"
            }
        ],
        "id": "3"
    }
]

猜你喜欢

转载自blog.csdn.net/zhangpan_soft/article/details/82887827