Java将对象列表转成树结构的两种方法
应用背景
在系统开发过程中,可能会碰到一些需求,需要构建树形结构,数据库一般就使用父id来表示,比如构建菜单、构建地区级联、构建部门层级结构等等。虽然可以通过数据库SQL查询,但我们一般都是通过SQL一次性查询出所有数据,在程序中处理成树形结构。
使用lambda表达式+递归
构建树的对象
@Data
public class Menu extends TreeEntity<Menu> {
public Menu(Long id, Long parentId, String title, Integer level) {
this.setId(id);
this.setParentId(parentId);
this.title = title;
this.level = level;
}
private String title;
private Integer level;
}
@Data
public class TreeEntity<T> {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 父菜单ID
*/
private Long parentId;
/**
* 子部门
*/
private List<T> children = new ArrayList<>();
}
生成树的工具类
public class ForestNodeMerger {
//将节点数组归并为一个森林(多棵树)(填充节点的children域)
public static <T extends TreeEntity<T>> List<T> merge(List<T> items) {
//获取parentId = -1l的根节点
List<T> list = items.stream().filter(item -> item.getParentId() == -1L).collect(Collectors.toList());
//根据parentId进行分组
Map<Long, List<T>> map = items.stream().collect(Collectors.groupingBy(TreeEntity::getParentId));
recursionFnTree(list, map);
return list;
}
public static <T extends TreeEntity<T>> void recursionFnTree(List<T> list, Map<Long, List<T>> map) {
for (T treeSelect : list) {
List<T> childList = map.get(treeSelect.getId());
if (null != childList && 0 < childList.size()) {
treeSelect.setChildren(childList);
recursionFnTree(childList, map);
} else {
treeSelect.setChildren(null);
}
}
}
}
示例
@Test
public void t1() throws Exception {
Menu menu1 = new Menu(1L, -1L, "aa", 1);
Menu menu2 = new Menu(2L, -1L, "bb", 1);
Menu menu3 = new Menu(3L, 1L, "cc", 2);
Menu menu4 = new Menu(4L, 1L, "dd", 2);
List<Menu> menuList = Arrays.asList(menu1, menu2, menu3, menu4);
List<Menu> mergeList = ForestNodeMerger.merge(menuList);
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(mergeList);
System.out.println(jsonStr);
}
[
{
"id": 1,
"parentId": -1,
"children": [
{
"id": 3,
"parentId": 1,
"children": null,
"title": "cc",
"level": 2
},
{
"id": 4,
"parentId": 1,
"children": null,
"title": "dd",
"level": 2
}
],
"title": "aa",
"level": 1
},
{
"id": 2,
"parentId": -1,
"children": null,
"title": "bb",
"level": 1
}
]
使用Hutool的TreeUtil
加入hutool的依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.18</version>
</dependency>
示例
@Test
public void t2() throws Exception {
Menu menu1 = new Menu(1L, -1L, "aa", 1);
Menu menu2 = new Menu(2L, -1L, "bb", 1);
Menu menu3 = new Menu(3L, 1L, "cc", 2);
Menu menu4 = new Menu(4L, 1L, "dd", 2);
List<Menu> menuList = Arrays.asList(menu1, menu2, menu3, menu4);
TreeNodeConfig config = new TreeNodeConfig();
//id
config.setIdKey("id");
//父id
config.setParentIdKey("parentId");
//排序字段
// config.setWeightKey("sort");
List<Tree<Long>> treeList = TreeUtil.build(menuList, -1L, config, ((object, treeNode) -> {
//对key进行映射赋值
treeNode.putExtra("id", object.getId());
treeNode.putExtra("parentId", object.getParentId());
treeNode.putExtra("title", object.getTitle());
treeNode.putExtra("level", object.getLevel());
}));
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = objectMapper.writeValueAsString(treeList);
System.out.println(jsonStr);
}
//[{"id":1,"parentId":-1,"title":"aa","level":1,"children":[{"id":3,"parentId":1,"title":"cc","level":2},{"id":4,"parentId":1,"title":"dd","level":2}]},{"id":2,"parentId":-1,"title":"bb","level":1}]