这里我以生成任务树为例子,写一下代码思路:
一、json格式
树的格式应该是这样的(json):
分析:
- 根任务的parentId是0,子任务的parentId是父任务的id
- 根任务和子任务的rootId,即根节点id相同
- 这是一种递归的结构,因为每个任务可以有子任务,这些子任务又可以有自己的子任务,依此类推。
- 注意:由于一个父任务可以对应多个子任务,这里“children”应该是一个集合。如果你想一个父任务对应一个子任务,那么“children”是一个对象。
二、类设计
这里我们采用分层的架构设计:
首先,根据json格式的任务树,那么与前端交互的VO(View Object)层:
中间的Model层:
与数据库交互的DO (Data Object)层:
三、树的结构分析
以下是一个任务树的例子:
分析:
- 有4个任务,形成了这样的一个树的关系
- 根任务的parentId是0,子任务的parentId是父任务的id
- 根任务和子任务的rootId,即根节点id相同
四、代码编写
思路:
- 第一步:找出这棵树的所有任务,即在数据库中找出所有rootId是10的任务。
- 第二步:按照parentId,找出其对应的子任务集合。
- 第三步:生成树,进行分层转换,将Model层转换为VO层返回给前端。
- 第四步:找出树中的第一个任务,即根任务。
- 第五步:根任务调用分层转换方法,返回任务树。
第一步:在数据库中找出所有rootId是10的任务(略),最后得到List<Task> tasks。
第二步:这里我们用HashMap,通过将任务按照它们的parentId进行归类,我们可以迅速找到任何父任务的所有直接子任务。代码如下:
这样,我们就把Id和这个Id对应的子任务集合对应起来了,遍历过程如下图:
这段代码中,我遇到了两个问题:
第一个:为什么要判断tasksWithSameParent是否为空?
答:如果tasksWithSameParent为空,那么tasksWithSameParent.add(task),会报空指针异常。
第二个:为什么要用parentIdMap.get(parentId)?
答:如上述代码第四步,我们已经有了key:parentId = 1,接下来我们又有一个任务是parentId = 1,这时候parentIdMap.get(parentId)这行代码就有用了,它会判断,我们现在是否有这个parentId,就会在原来集合的基础上添加上当前任务。
当然,以上代码还可以用lambda表达式简化:
第三步:生成树,将Model转换为VO返回给前端。
这里运用了递归的转换,代码如下:
当然,这段代码也可以用lambda表达式:
第四步:找出树中的第一个任务,即根任务。
List<Task> rootTask = parentIdMap.get(0L);
第五步:根任务调用转换方法,返回任务树。
return rootTask.stream().map(it -> converterModelToTreeVO(it, parentIdMap)).collect(Collectors.toList());
这里读不懂lambda表达式的话,可以看我之前的文章一文搞定!lambda表达式!
以上就是如何生成一颗树的心得了,有用的话请点个赞吧!