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

  • 因为项目需求,修改将如下数据格式的数据转为树结构数据:
idparent
1null
2null
3null
41
51
61
72
82
93
104
117
125
1310
148
1511
1612
1713
  • 将这样拥有父子依赖关系的数据转化为多叉树,本来想在网上找个现成代码的,百度半天,发现,说的云的雾的,思路感觉都很混乱,因此决定自己动手造轮子,因为我是管理整个项目架构的,所以这时候如果这个地方用,写一个,呢个地方用再写一个,明显不太科学,因此,我做了一个工具类,此工具类目前只提供2种功能,其一就是将目标集合转成多叉树集合,其二就是给目标节点添加孩子,至于其他的功能,因为项目中用不到,没有进行封装,后续如果有小伙伴需要用到多叉树的遍历,查找,排序,插入,删除功能的话再进行封装,大家需要用到这些功能可以给我留言,我在闲暇时间可能给你们封装一套
  • 好了废话少说,先上代码,代码中注释非常清晰,几乎逐行注释,所以在这里不多做代码的解释
package com.rjhcsoft.credit.utils.tree;

import com.alibaba.fastjson.JSON;
import com.rjhcsoft.credit.utils.StringUtil;

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);
    }

    /**
     * 集合转树结构,注意,使用此方法,则集合元素必须继承ITree接口
     * @param collection 目标集合
     * @param <T>
     * @return
     */
    public static <T extends ITree> Collection<T> toTree(@NotNull Collection<T> collection){
        try {
            if (collection==null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
            // 找出所有的根节点
            Collection<T> roots = null;
            if (collection.getClass().isAssignableFrom(Set.class)) roots = new HashSet<>();
            else roots = new ArrayList<>();
            for (T tree:collection){
                Object o = ITree.class.getMethod("getParent").invoke(tree);
                if (o instanceof String){
                    if (StringUtil.isEmpty((String) o)){
                        roots.add(tree);
                    }
                }else if (o == null){
                    roots.add(tree);
                }
            }
            // 从目标集合移除所有的根节点
            collection.removeAll(roots);
            // 为根节点添加孩子节点
            for (T tree:roots){
                addChild(tree,collection);
            }
            return roots;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 集合转树结构
     * @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 = null;
            try {
                idField=clazz.getDeclaredField(id);// 获取依赖字段
            }catch (NoSuchFieldException e1){
                idField=clazz.getSuperclass().getDeclaredField(id);
            }
            Field parentField = null;
            try {
                parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            }catch (NoSuchFieldException e1){
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }
            Field childrenField = null;// 获取孩子字段
            try {
                childrenField=clazz.getDeclaredField(children);
            }catch (NoSuchFieldException e1){
                childrenField=clazz.getSuperclass().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);
        }
    }

    public static <T extends ITree> void addChild(T tree,Collection<T> collection){
        try {
            Object id = ITree.class.getMethod("getId").invoke(tree);
            Collection<T> children = (Collection<T>) ITree.class.getMethod("getChildren").invoke(tree);
            for (T cc:collection){
                Object o = ITree.class.getMethod("getParent").invoke(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一切皆指针
                    ITree.class.getMethod("setChildren", Collection.class).invoke(tree,children);
                    // 递归添加孩子节点
                    addChild(cc,collection);
                }
            }
        } 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 = null;
            try {
                idField=clazz.getDeclaredField(id);// 获取依赖字段
            }catch (NoSuchFieldException e1){
                idField=clazz.getSuperclass().getDeclaredField(id);
            }
            Field parentField = null;
            try {
                parentField = clazz.getDeclaredField(parent);// 获取被依赖字段
            }catch (NoSuchFieldException e1){
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }
            Field childrenField = null;// 获取孩子字段
            try {
                childrenField=clazz.getDeclaredField(children);
            }catch (NoSuchFieldException e1){
                childrenField=clazz.getSuperclass().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;
    }
}


  • ITree
package com.rjhcsoft.credit.utils.tree;

import java.io.Serializable;
import java.util.Collection;

public interface ITree {
    /**
     * 获取被依赖节点
     * @return
     */
    Serializable getId();

    /**
     * 设置被依赖节点
     * @param id
     */
    void setId(Serializable id);

    /**
     * 获取依赖节点
     * @return
     */
    Serializable getParent();

    /**
     * 设置依赖节点
     * @param parent
     */
    void setParent(Serializable parent);

    /**
     * 获取孩子列表
     * @return
     */
    Collection<? extends ITree> getChildren();

    /**
     * 设置孩子列表
     * @param children
     */
    void setChildren(Collection<? extends ITree> children);
}

  • 最终打印的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"
    }
]
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Java 工具类,可以将 List 换为树形结构: ```java import java.util.*; public class TreeUtil { public static <T extends TreeNode> List<T> buildTree(List<T> nodes) { if (nodes == null || nodes.size() == 0) { return Collections.emptyList(); } Map<Long, T> nodeMap = new HashMap<>(); for (T node : nodes) { nodeMap.put(node.getId(), node); } List<T> rootNodes = new ArrayList<>(); for (T node : nodes) { T parent = nodeMap.get(node.getParentId()); if (parent != null) { parent.addChild(node); } else { rootNodes.add(node); } } return rootNodes; } public interface TreeNode { Long getId(); Long getParentId(); void addChild(TreeNode child); List<? extends TreeNode> getChildren(); } } ``` 这个工具类包含了一个通用的接口 `TreeNode`,通过实现这个接口,可以将任意类型的 List 换为树形结构。 `TreeNode` 接口包含了三个方法: - `getId()`:获取节点的唯一标识符。 - `getParentId()`:获取节点的父节点标识符。 - `addChild(TreeNode child)`:将一个子节点添加到当前节点。 - `getChildren()`:获取当前节点的所有子节点。 使用这个工具类非常简单,只需要将需要换的 List 传入 `buildTree()` 方法中即可: ```java List<MyNode> nodes = ...; // 获取需要换的 List List<MyNode> rootNodes = TreeUtil.buildTree(nodes); ``` 其中 `MyNode` 是一个实现了 `TreeNode` 接口的自定义类。注意,为了能够正确地构建树形结构,每个节点的 `getParentId()` 方法必须返回其父节点的 `getId()` 值。否则,节点将无法正确地添加到树中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值