Java递归、非递归构建具有无线层级父子关系的目录、菜单

1、背景

  项目开发中,比如一本书,具有多级目录;一个一级分类下面还有二级分类,二级下面有三级…。此时需要后端返回一颗完整的无限层级的菜单树。

2、思路

  构建树的一般思想都是使用Map之类的映射处理或者使用递归之类的处理,Map有个好处是直观明了,具体实现可见本人的另外一篇(Java使用Map构建无限层级树)。但是递归对数据保护的比较好。本处介绍一下使用递归来构建树:

  • 找出所有的顶级父级,组装成List。
  • 递归找出每一个顶级父级的孩子节点。

3、实现

3.1、递归实现

import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 说明:构建无限层级树
 *
 * @author: Fisher.Hao
 * @date: 2021-07-24 19:20:12
 */
public class Test {

	public static void main(String[] args) {

		//模仿数据库中查询出来的数据
		List<Person> dataIsSort = getData(true);
		List<Person> dataNoSort = getData(false);

		//获取顶级父级节点
		List<Person> parentListIsSort = getParentList(dataIsSort);
		List<Person> parentListNoSort = getParentList(dataNoSort);

		//排序
		for (Person parent : parentListIsSort) {
			parent.setChildren(getChildrenList(dataIsSort, parent));
		}

		//不排
		for (Person parent : parentListNoSort) {
			parent.setChildren(getChildrenList(dataNoSort, parent));
		}

		System.out.println("排序后的JSON:************************************************************************");
		System.out.println(JSON.toJSONString(parentListIsSort));
		System.out.println("不排序后的JSON:************************************************************************");
		System.out.println(JSON.toJSONString(parentListNoSort));
	}

	/**
	 * 获取所有的顶级父级节点
	 *
	 * @param allPersonList
	 * 		所有节点
	 * @return 返回所有的顶级父节点
	 */
	private static List<Person> getParentList(final List<Person> allPersonList) {
		//顶级父级节点的parent为0
		return allPersonList.stream().filter(p -> Objects.equals(p.getParentId(), 0)).collect(Collectors.toList());
	}

	/**
	 * 获取顶级父节点的孩子节点
	 *
	 * @param allPersonList
	 * 		所有的数据
	 * @param parent
	 * 		顶级父
	 * @return 孩子节点
	 */
	private static List<Person> getChildrenList(final List<Person> allPersonList, final Person parent) {

		//遍历所有的节点,获取当前父节点的所以的孩子节点
		List<Person> childrenList = allPersonList.stream().filter(p -> Objects.equals(p.getParentId(), parent.getId())).collect(Collectors.toList());

		//递归处理:找到孩子节点的孩子节点,直至其没有孩子节点
		for (Person child : childrenList) {

			//当前孩子节点child作为父节点继续遍历
			child.setChildren(getChildrenList(allPersonList, child));
		}

		//返回孩子节点,此时孩子节点的孩子节点都已经填充满了
		return childrenList;
	}

	/**
	 * 构造带有父子关系的数据
	 *
	 * @param isSort
	 * 		是否排序,模仿需要指定顺序的情况,此处指定为从小到大
	 * @return 初始化数据
	 */
	private static List<Person> getData(boolean isSort) {

		Person p1 = new Person(1, 0, "顶级父1");
		Person p3 = new Person(3, 1, "子1_3");
		Person p4 = new Person(4, 1, "子1_4");

		Person p21 = new Person(21, 3, "子1_3_21");
		Person p22 = new Person(22, 4, "子1_4_22");
		Person p23 = new Person(23, 4, "子1_4_23");
		Person p24 = new Person(24, 23, "子1_4_23_24");

		Person p5 = new Person(5, 1, "子1_5");
		Person p6 = new Person(6, 1, "子1_6");
		Person p7 = new Person(7, 1, "子1_7");

		Person p2 = new Person(2, 0, "顶级父2");
		Person p8 = new Person(8, 2, "子2_8");

		Person p14 = new Person(14, 8, "子2_8_14");
		Person p15 = new Person(15, 8, "子2_8_15");
		Person p17 = new Person(17, 8, "子2_8_17");

		Person p18 = new Person(18, 14, "子2_8_14_18");
		Person p19 = new Person(19, 14, "子2_8_14_19");
		Person p20 = new Person(20, 15, "子2_8_15_20");

		Person p9 = new Person(9, 2, "子2_9");
		Person p10 = new Person(10, 2, "子2_10");
		Person p11 = new Person(11, 2, "子2_11");
		Person p12 = new Person(12, 2, "子2_12");
		Person p13 = new Person(13, 2, "子2_13");

		Person p16 = new Person(16, 0, "顶级父16");

		List<Person> list = new ArrayList<Person>();

		//打乱排序
		list.add(p15);
		list.add(p14);
		list.add(p8);

		list.add(p23);

		list.add(p11);
		list.add(p12);
		list.add(p5);
		list.add(p13);
		list.add(p18);
		list.add(p19);

		list.add(p24);

		list.add(p10);
		list.add(p17);

		list.add(p7);
		list.add(p9);
		list.add(p21);
		list.add(p4);
		list.add(p16);
		list.add(p6);

		list.add(p20);
		list.add(p22);
		list.add(p1);
		list.add(p2);
		list.add(p3);

		if (isSort) {
			return list.stream().distinct().sorted(Comparator.comparingInt(Person::getId)).collect(Collectors.toList());
		}

		return list.stream().distinct().collect(Collectors.toList());
	}
}

class Person {

	/**
	 * 主id,不可能为空
	 */
	private Integer id;

	/**
	 * 父id,当其为0时表示为顶级父
	 */
	private Integer parentId;

	private String name;

	/**
	 * 孩子节点
	 */
	private List<Person> children;

	public Person(Integer id, Integer parentId, String name) {
		this.id = id;
		this.parentId = parentId;
		this.name = name;
	}

	public Integer getParentId() {
		return parentId;
	}

	public void setParentId(Integer parentId) {
		this.parentId = parentId;
	}

	public Integer getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

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

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

}

3.2、非递归实现

    本处的初始化数据沿用,递归实现时候的数据。

	private static void method_2() {

		//获取模拟数据库的数据
		List<Person> dataList = getData(false);

		//将List转化为Map,其中id为key,对象为value
		Map<Integer, Person> personMap = dataList.stream().collect(Collectors.toMap(Person::getId, Function.identity()));

		//重新构建List
		List<Person> list = new ArrayList<>();

		//原List遍历
		for (Person person : dataList) {

			//获取当前对象的父级id
			Integer parentId = person.getParentId();

			//获取该父级id,对应的Person对象
			Person parentPerson = personMap.get(parentId);

			//父级对象为空,表明parentPerson为顶级父级对象,直接将其放入list:本循环运行完后,list的大小其实就是顶级父级的个数
			if (parentPerson == null) {
				list.add(person);
			} else {

				//获取父级对象的子节点
				List<Person> children = parentPerson.getChildren();

				//孩子节点为空,则初始化孩子节点,并将当前节点插入到父节点的孩子节点中
				if (Objects.isNull(children) || children.isEmpty()) {

					//初始化孩子节点
					List<Person> tempList = new ArrayList<>();
					tempList.add(person);

					//添加到孩子节点
					parentPerson.setChildren(tempList);
				} else {
					//孩子节点不为空,直接把当前节点插入到父级节点的子节点中
					children.add(person);
				}
			}
		}

		System.out.println(list);
		System.out.println("最后构建的菜单树为:*************************");
		System.out.println(JSON.toJSONString(list));
	}

4、运行结果

排序后的JSON:************************************************************************
[{"children":[{"children":[{"children":[],"id":21,"name":"子1_3_21","parentId":3}],"id":3,"name":"子1_3","parentId":1},{"children":[{"children":[],"id":22,"name":"子1_4_22","parentId":4},{"children":[{"children":[],"id":24,"name":"子1_4_23_24","parentId":23}],"id":23,"name":"子1_4_23","parentId":4}],"id":4,"name":"子1_4","parentId":1},{"children":[],"id":5,"name":"子1_5","parentId":1},{"children":[],"id":6,"name":"子1_6","parentId":1},{"children":[],"id":7,"name":"子1_7","parentId":1}],"id":1,"name":"顶级父1","parentId":0},{"children":[{"children":[{"children":[{"children":[],"id":18,"name":"子2_8_14_18","parentId":14},{"children":[],"id":19,"name":"子2_8_14_19","parentId":14}],"id":14,"name":"子2_8_14","parentId":8},{"children":[{"children":[],"id":20,"name":"子2_8_15_20","parentId":15}],"id":15,"name":"子2_8_15","parentId":8},{"children":[],"id":17,"name":"子2_8_17","parentId":8}],"id":8,"name":"子2_8","parentId":2},{"children":[],"id":9,"name":"子2_9","parentId":2},{"children":[],"id":10,"name":"子2_10","parentId":2},{"children":[],"id":11,"name":"子2_11","parentId":2},{"children":[],"id":12,"name":"子2_12","parentId":2},{"children":[],"id":13,"name":"子2_13","parentId":2}],"id":2,"name":"顶级父2","parentId":0},{"children":[],"id":16,"name":"顶级父16","parentId":0}]
不排序后的JSON:************************************************************************
[{"children":[],"id":16,"name":"顶级父16","parentId":0},{"children":[{"children":[],"id":5,"name":"子1_5","parentId":1},{"children":[],"id":7,"name":"子1_7","parentId":1},{"children":[{"children":[{"children":[],"id":24,"name":"子1_4_23_24","parentId":23}],"id":23,"name":"子1_4_23","parentId":4},{"children":[],"id":22,"name":"子1_4_22","parentId":4}],"id":4,"name":"子1_4","parentId":1},{"children":[],"id":6,"name":"子1_6","parentId":1},{"children":[{"children":[],"id":21,"name":"子1_3_21","parentId":3}],"id":3,"name":"子1_3","parentId":1}],"id":1,"name":"顶级父1","parentId":0},{"children":[{"children":[{"children":[{"children":[],"id":20,"name":"子2_8_15_20","parentId":15}],"id":15,"name":"子2_8_15","parentId":8},{"children":[{"children":[],"id":18,"name":"子2_8_14_18","parentId":14},{"children":[],"id":19,"name":"子2_8_14_19","parentId":14}],"id":14,"name":"子2_8_14","parentId":8},{"children":[],"id":17,"name":"子2_8_17","parentId":8}],"id":8,"name":"子2_8","parentId":2},{"children":[],"id":11,"name":"子2_11","parentId":2},{"children":[],"id":12,"name":"子2_12","parentId":2},{"children":[],"id":13,"name":"子2_13","parentId":2},{"children":[],"id":10,"name":"子2_10","parentId":2},{"children":[],"id":9,"name":"子2_9","parentId":2}],"id":2,"name":"顶级父2","parentId":0}]

运行图示

5、JSON图示

图示
在这里插入图片描述
在这里插入图片描述
运行图示

6、格式化展示

[
    {
        "children":[
            {
                "children":[
                    {
                        "children":[

                        ],
                        "id":21,
                        "name":"子1_3_21",
                        "parentId":3
                    }
                ],
                "id":3,
                "name":"子1_3",
                "parentId":1
            },
            {
                "children":[
                    {
                        "children":[

                        ],
                        "id":22,
                        "name":"子1_4_22",
                        "parentId":4
                    },
                    {
                        "children":[
                            {
                                "children":[

                                ],
                                "id":24,
                                "name":"子1_4_23_24",
                                "parentId":23
                            }
                        ],
                        "id":23,
                        "name":"子1_4_23",
                        "parentId":4
                    }
                ],
                "id":4,
                "name":"子1_4",
                "parentId":1
            },
            {
                "children":[

                ],
                "id":5,
                "name":"子1_5",
                "parentId":1
            },
            {
                "children":[

                ],
                "id":6,
                "name":"子1_6",
                "parentId":1
            },
            {
                "children":[

                ],
                "id":7,
                "name":"子1_7",
                "parentId":1
            }
        ],
        "id":1,
        "name":"顶级父1",
        "parentId":0
    },
    {
        "children":[
            {
                "children":[
                    {
                        "children":[
                            {
                                "children":[

                                ],
                                "id":18,
                                "name":"子2_8_14_18",
                                "parentId":14
                            },
                            {
                                "children":[

                                ],
                                "id":19,
                                "name":"子2_8_14_19",
                                "parentId":14
                            }
                        ],
                        "id":14,
                        "name":"子2_8_14",
                        "parentId":8
                    },
                    {
                        "children":[
                            {
                                "children":[

                                ],
                                "id":20,
                                "name":"子2_8_15_20",
                                "parentId":15
                            }
                        ],
                        "id":15,
                        "name":"子2_8_15",
                        "parentId":8
                    },
                    {
                        "children":[

                        ],
                        "id":17,
                        "name":"子2_8_17",
                        "parentId":8
                    }
                ],
                "id":8,
                "name":"子2_8",
                "parentId":2
            },
            {
                "children":[

                ],
                "id":9,
                "name":"子2_9",
                "parentId":2
            },
            {
                "children":[

                ],
                "id":10,
                "name":"子2_10",
                "parentId":2
            },
            {
                "children":[

                ],
                "id":11,
                "name":"子2_11",
                "parentId":2
            },
            {
                "children":[

                ],
                "id":12,
                "name":"子2_12",
                "parentId":2
            },
            {
                "children":[

                ],
                "id":13,
                "name":"子2_13",
                "parentId":2
            }
        ],
        "id":2,
        "name":"顶级父2",
        "parentId":0
    },
    {
        "children":[

        ],
        "id":16,
        "name":"顶级父16",
        "parentId":0
    }
]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值