欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

封装树形工具类,将list转化为树形结构

程序员文章站 2024-02-01 14:47:40
...

工具类

后续将继续优化

public class TreeUtils {

    private static final String ROOT_ID = "0";
    private static final String GET = "get";
    private static final String SET = "set";

    /**
     * 私有构造器
     */
    private TreeUtils() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * 使用递归方法建树
     * (如果项目中的各个表的id、父id、封装子对象的list名称均是固定,则可以再次简化,将反射获取属性的过程直接写
     * 死,这样调用时只需传list即可)
     * @param list         需要转化树形的list
     * @param idName       实体类中id的属性名(首字母需要大写,例如属性为id,则传Id)
     * @param parentIdName 实体类中父id的属性名(首字母需要大写,例如属性为parentId,则传ParentId)
     * @param childrenName 实体类中封装树形的子list方法名(首字母需要大写,例如属性为list,则传List)
     * @return 树形list
     */
    public static <T> List<T> buildByRecursive(List<T> list, String idName, String parentIdName, String childrenName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<T> result = new ArrayList<>();
        //遍历封装pid=0的
        for (T t : list) {
            //根据反射,获取父id的值
            String pId = reflect(t.getClass(), GET + parentIdName, t).toString();
            //如果pd=0,即是首层
            if (ROOT_ID.equals(pId)) {
                //递归查询并封装子对象
                result.add(findChildren(t, list, idName, parentIdName, childrenName));
            }
        }
        return result;
    }

    /**
     * 递归查找子节点
     *
     * @param idName       实体类中id的属性名(首字母需要大写)
     * @param parentIdName 实体类中父id的属性名(首字母需要大写)
     * @param bean
     * @param beans
     * @return
     */
    public static <T> T findChildren(T bean, List<T> beans, String idName, String parentIdName, String childrenName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //获取bean的id, bean.getId()
        String id = bean.getClass().getMethod(GET + idName).invoke(bean).toString();
        //获取bean用来封装子对象的list, bean.getList()
        List<T> children = (List) reflect(bean.getClass(), GET + childrenName, bean);
        //递归遍历
        for (T it : beans) {
            //根据反射,获取it的pId的值 it.getParentId()
            String parentId = it.getClass().getMethod(GET + parentIdName).invoke(it).toString();
            //如果是其子对象
            if (id.equals(parentId)) {
                //如果封装子对象的list为空,则创建
                if (children == null) {
                    bean.getClass().getMethod(SET + childrenName, List.class).invoke(bean, new ArrayList<>());
                }
                //重新获取子对象的list
                children = (List) reflect(bean.getClass(), GET + childrenName, bean);
                //继续递归遍历
                children.add(findChildren(it, beans, idName, parentIdName, childrenName));
            }
        }
        return bean;
    }

    /**
     * 根据反射获取属性值
     *
     * @param clazz      实体类名
     * @param methodName 方法名
     * @param t          对象
     * @param <T>        对象类
     * @return
     */
    public static <T> Object reflect(Class clazz, String methodName, T t) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        return clazz.getMethod(methodName).invoke(t);
    }

}

测试

数据库结构
封装树形工具类,将list转化为树形结构

实体类


```java
/**
 * 角色
 *
 */
@Data
public class SysRoleEntity implements Serializable {
	/**
	 * 角色ID
	 */
	@TableId
	private Long roleId;
	/**
	 * 父类ID
	 */
	private Long parentId;
	/**
	 * 角色名称
	 */
	private String roleName;
	/**
	 * 封装子角色的list
	 */
	private List<SysRoleEntity> children;

}

测试方法

@GetMapping("/test")
	public List<SysRoleEntity> test() throws Exception{
		//第一步查询所有数据(普通的查询所有,就不再一层一层的向下写了)
		List<SysRoleEntity> list = sysRoleService.selectAll();
		//调用工具转化为树形
		List<SysRoleEntity> result = buildByRecursive(list, "RoleId", "ParentId", "Children");
		return result;
	}

结果

[{
		"roleId": 1,
		"parentId": 0,
		"roleName": "技术部总监",
		"children": [{
				"roleId": 4,
				"parentId": 1,
				"roleName": "JAVA开发",
				"children": [{
						"roleId": 12,
						"parentId": 4,
						"roleName": "JAVA大牛"
					},
					{
						"roleId": 13,
						"parentId": 4,
						"roleName": "java菜鸟"
					}
				]
			},
			{
				"roleId": 5,
				"parentId": 1,
				"roleName": "IOS开发"
			},
			{
				"roleId": 6,
				"parentId": 1,
				"roleName": "安卓开发"
			},
			{
				"roleId": 7,
				"parentId": 1,
				"roleName": "前端开发"
			}
		]
	},
	{
		"roleId": 2,
		"parentId": 0,
		"roleName": "人事部总监",
		"children": [{
				"roleId": 8,
				"parentId": 2,
				"roleName": "人事专员"
			},
			{
				"roleId": 9,
				"parentId": 2,
				"roleName": "前台"
			}
		]
	},
	{
		"roleId": 3,
		"parentId": 0,
		"roleName": "财务总监",
		"children": [{
				"roleId": 10,
				"parentId": 3,
				"roleName": "会计"
			},
			{
				"roleId": 11,
				"parentId": 3,
				"roleName": "出纳"
			}
		]
	}
]

可以看到,子角色已经封装到父角色下。

结尾

实际就是一个通用的递归封装对象,后续将继续进行优化,如果有大佬有更好的方法欢迎告知。