开发小技巧系列文章,是本人对过往平台系统的设计开发及踩坑的记录与总结,给初入平台系统开发的开发人员提供参考与帮助。
在开发的过程中,有时候需要将集合的数据转换成一个树形结构,比如功能菜单、组织机构、或者商品分类等的场景。一般这些数据在数据表的存储中,都是采用行的方式来存储数据,方便对数据进行管理,在表的字段上会有“父ID(parent_id)”的字段,来表示关系。
假设有如下表结构及数据:
需要转换成一个树形结构,预期结构:
如何用java程序来实现呢?可能很多人会想到用递归来实现,这也是最常见的方式,可以实现一棵无限级的树形结果。是否还有其他实现方式呢?让我们先从递归实现来说起。
首先,需要定义一个树形结构的对象,如下:TreeVo.java
package net.jhelp.demo.tree;
import lombok.Data;
import java.util.List;
/**
*
*
* @author : kame
* @date: 2022/4/9 10:30 AM
*/
@Data
public class TreeVo {
private Integer id;
private String name;
private List<TreeVo> nodeList;
public TreeVo(Integer id, String name) {
this.id = id;
this.name = name;
}
}
树形转换工具类 TreeKit.java , 递归函数的实现:
/**
* list集合的行数据,转换成 tree 结构
* @param beans
* @return
*/
public static List<TreeVo> list2Tree(List<Table> beans) {
List<TreeVo> result = new ArrayList<>();
if(!CollectionUtils.isEmpty(beans)){
for(Table p : beans){
if(p.getParentId() == null || p.getParentId() == 0) {
TreeVo vo = new TreeVo(p.getId(),p.getName());
vo.setNodeList(recurrence(beans, p));
result.add(vo);
}
}
}
return result;
}
/**
* 递归函数
* @param list
* @param vo
*/
private static List<TreeVo> recurrence(List<Table> list, Table vo){
List<TreeVo> subNodes = new ArrayList<>();
for(Table d : list){
if (vo.getId().equals(d.getParentId())) {
TreeVo sub = new TreeVo(d.getId(), d.getName());
sub.setNodeList(recurrence(list, d));
subNodes.add(sub);
}
}
return subNodes;
}
测试用例:
//模拟数据库查询出来的结果
public static
List<Table> tableData = Arrays.asList(
new Table(1, 0, "根节点"),
new Table(2, 1, "子节点1"),
new Table(3, 2, "子节点1.1"),
new Table(4, 2, "子节点1.2"),
new Table(5, 2, "子节点1.3"),
new Table(6, 1, "子节点2"),
new Table(7, 6, "子节点2.1"),
new Table(8, 6, "子节点2.2"),
new Table(9, 1, "子节点3"),
new Table(10, 9, "子节点3.1")
);
@Test
public void Bo2TreeTest(){
List<TreeVo> result = TreeKit.list2Tree(tableData);
log.info("递归:转换结果:{}", JsonUtil.toJson(result));
}
执行结果为:
[
{
"id":1,
"name":"根节点",
"nodeList":[
{
"id":2,
"name":"子节点1",
"nodeList":[
{
"id":3,
"name":"子节点1.1",
"nodeList":[
]
},
{
"id":4,
"name":"子节点1.2",
"nodeList":[
]
},
{
"id":5,
"name":"子节点1.3",
"nodeList":[
]
}
]
},
{
"id":6,
"name":"子节点2",
"nodeList":[
{
"id":7,
"name":"子节点2.1",
"nodeList":[
]
},
{
"id":8,
"name":"子节点2.2",
"nodeList":[]
}
]
},
{
"id":9,
"name":"子节点3",
"nodeList":[
{
"id":10,
"name":"子节点3.1",
"nodeList":[
]
}
]
}
]
}
]
上面的程序都是使用For循环来实现List集合转换到Tree结构,大家是否还记得JDK8的一个新特性 -- Lambda表达式, 可能使用Stream来简化处理,那么程序会是怎么样呢?用Stream流式的代码:
public static List<TreeVo> toTree(List<Table> datas){
//得到父节点
List<TreeVo> roots = datas.stream()
.filter(m -> m.getParentId() == null || m.getParentId() == 0)
.map(m -> {
TreeVo vo = new TreeVo(m.getId(), m.getName());
vo.setNodeList(buildSubNodes(m, datas));
return vo;
})
.collect(Collectors.toList());
return roots;
}
private static List<TreeVo> buildSubNodes(Table root, List<Table> list){
List<TreeVo> subNodes = list.stream()
.filter(m -> Objects.equals(root.getId(), m.getParentId()))
.map(m -> {
TreeVo vo = new TreeVo(m.getId(), m.getName());
vo.setNodeList(buildSubNodes(m, list));
return vo;
}).collect(Collectors.toList());
return subNodes;
}
测试用例:
@Test
public void Bo2TreeTest2(){
List<TreeVo> result = TreeKit.toTree(tableData);
log.info("Stream流:转换结果:{}", JsonUtil.toJson(result));
}
输出结果:
[
{
"id":1,
"name":"根节点",
"nodeList":[
{
"id":2,
"name":"子节点1",
"nodeList":[
{
"id":3,
"name":"子节点1.1",
"nodeList":[
]
},
{
"id":4,
"name":"子节点1.2",
"nodeList":[
]
},
{
"id":5,
"name":"子节点1.3",
"nodeList":[
]
}
]
},
{
"id":6,
"name":"子节点2",
"nodeList":[
{
"id":7,
"name":"子节点2.1",
"nodeList":[ ]
},
{
"id":8,
"name":"子节点2.2",
"nodeList":[]
}
]
},
{
"id":9,
"name":"子节点3",
"nodeList":[
{
"id":10,
"name":"子节点3.1",
"nodeList":[ ]
}
]
}
]
}
]
从输出的结果来看,流式的处理方式,一样也可以实现将List集合转化成Tree的结果。如果读者还有更好的方式,欢迎留言讨论。
如果需要测试用例的代码,可以访问:java-tree-demo: java程序生成树形结构的例子
更多内容,欢迎关注订阅号:技术老男孩。