一般情况下,我们想要实现这种无限层级的树形结构,都是采用递归的方式,但是递归比较占用内存,也容易导致栈溢出,于是只能尝试其它的方法。
下面采用的方式,只需要一次集合的遍历就可以实现树形的结构。
先手动编造一个树形结构的数据:
这是一个无序的树形结构数据的 json 格式:
[
{
"nodeId" : "700",
"nodeDesc" : "节点700",
"parentNodeId" : ""
},
{
"nodeId" : "300",
"nodeDesc" : "节点300",
"parentNodeId" : ""
},
{
"nodeId" : "500",
"nodeDesc" : "节点500",
"parentNodeId" : ""
},
{
"nodeId" : "100",
"nodeDesc" : "节点100",
"parentNodeId" : ""
},
{
"nodeId" : "400",
"nodeDesc" : "节点400",
"parentNodeId" : ""
},
{
"nodeId" : "800",
"nodeDesc" : "节点800",
"parentNodeId" : ""
},
{
"nodeId" : "200",
"nodeDesc" : "节点200",
"parentNodeId" : ""
},
{
"nodeId" : "700-001",
"nodeDesc" : "节点700-001",
"parentNodeId" : "700"
},
{
"nodeId" : "700-002",
"nodeDesc" : "节点700-002",
"parentNodeId" : "700"
},
{
"nodeId" : "700-003",
"nodeDesc" : "节点700-003",
"parentNodeId" : "700"
},
{
"nodeId" : "700-004",
"nodeDesc" : "节点700-004",
"parentNodeId" : "700"
},
{
"nodeId" : "700-005",
"nodeDesc" : "节点700-005",
"parentNodeId" : "700"
},
{
"nodeId" : "700-006",
"nodeDesc" : "节点700-006",
"parentNodeId" : "700"
},
{
"nodeId" : "700-003-002",
"nodeDesc" : "节点700-003-002",
"parentNodeId" : "700-003"
},
{
"nodeId" : "700-003-001",
"nodeDesc" : "节点700-003-001",
"parentNodeId" : "700-003"
},
{
"nodeId" : "700-003-002-001",
"nodeDesc" : "节点700-003-002-001",
"parentNodeId" : "700-003-002"
},
{
"nodeId" : "700-003-002-003",
"nodeDesc" : "节点700-003-002-003",
"parentNodeId" : "700-003-002"
},
{
"nodeId" : "700-003-002-002",
"nodeDesc" : "节点700-003-002-002",
"parentNodeId" : "700-003-002"
},
{
"nodeId" : "300-001",
"nodeDesc" : "节点300-001",
"parentNodeId" : "300"
},
{
"nodeId" : "300-001-001",
"nodeDesc" : "节点300-001-001",
"parentNodeId" : "300-001"
},
{
"nodeId" : "300-001-001-001",
"nodeDesc" : "节点300-001-001-001",
"parentNodeId" : "300-001-001"
},
{
"nodeId" : "300-001-001-001-001",
"nodeDesc" : "节点300-001-001-001-001",
"parentNodeId" : "300-001-001-001"
},
{
"nodeId" : "300-001-001-001-001-001",
"nodeDesc" : "节点300-001-001-001-001-001",
"parentNodeId" : "300-001-001-001-001"
},
{
"nodeId" : "500-003",
"nodeDesc" : "节点500-003",
"parentNodeId" : "500"
},
{
"nodeId" : "500-001",
"nodeDesc" : "节点500-001",
"parentNodeId" : "500"
},
{
"nodeId" : "500-002",
"nodeDesc" : "节点500-002",
"parentNodeId" : "500"
},
{
"nodeId" : "500-003-001",
"nodeDesc" : "节点500-003-001",
"parentNodeId" : "500-003"
},
{
"nodeId" : "800-001",
"nodeDesc" : "节点800-001",
"parentNodeId" : "800"
},
{
"nodeId" : "100-002",
"nodeDesc" : "节点100-002",
"parentNodeId" : "100"
},
{
"nodeId" : "100-001",
"nodeDesc" : "节点100-001",
"parentNodeId" : "100"
},
{
"nodeId" : "200-001",
"nodeDesc" : "节点200-001",
"parentNodeId" : "200"
},
{
"nodeId" : "400-003",
"nodeDesc" : "节点400-003",
"parentNodeId" : "400"
},
{
"nodeId" : "400-002",
"nodeDesc" : "节点400-002",
"parentNodeId" : "400"
},
{
"nodeId" : "400-001",
"nodeDesc" : "节点400-001",
"parentNodeId" : "400"
},
{
"nodeId" : "100-002-002",
"nodeDesc" : "节点100-002-002",
"parentNodeId" : "100-002"
},
{
"nodeId" : "100-002-001",
"nodeDesc" : "节点100-002-001",
"parentNodeId" : "100-002"
},
{
"nodeId" : "300-001-001-001-001-002",
"nodeDesc" : "节点300-001-001-001-001-002",
"parentNodeId" : "300-001-001-001-001"
},
{
"nodeId" : "300-001-001-002",
"nodeDesc" : "节点300-001-002",
"parentNodeId" : "300-001"
}
]
我是在springBoot项目中测试的,通过前端页面展示数形结构的效果。
这是后端项目结构,前端使用的 ant-design Vue 版。
<template>
<div>
<a-tree
:show-line="true"
:show-icon="true"
:tree-data="treeData"
:replaceFields="{children:'children', title:'nodeDesc', key:'nodeId' }"
@select="onSelect"
>
</a-tree>
</div>
</template>
<script>
import { queryTreeNodeList } from '@/services/myExample';
import { message } from 'ant-design-vue';
export default {
name: 'lemonTree',
data() {
return {
treeData: []
}
},
created() {
queryTreeNodeList().then(response =>{
console.log(response, '====response====')
this.treeData = response.data.data
})
},
methods: {
onSelect(selectedKeys, info) {
console.log('selected', selectedKeys, info);
// console.log('nodeDesc', info.selectedNodes[0].data.props.nodeDesc);
message.info('您点击了:' + info.selectedNodes[0].data.props.nodeDesc);
}
}
}
</script>
<style scoped>
</style>
构建树形结构的核心业务代码,都写在了 service 层的这个方法里:
package com.concat.example.service;
import com.alibaba.fastjson2.JSON;
import com.concat.example.entity.LemonTreeEntity;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class LemonTreeServiceImpl implements LemonTreeService {
@Override
public List<LemonTreeEntity> queryAllNodeWithTreeStructure() {
List<LemonTreeEntity> dataSource = initLemonTree();
// 如果要得到有顺序树状数据结构,则先进行排序操作,并且后面可以通过循环这个有序的集合组装最终的树状结构的数据;
List<LemonTreeEntity> lemonTreeNodeList = dataSource.stream().sorted(Comparator.comparing(LemonTreeEntity::getNodeId)).collect(Collectors.toList());
// 建立每个节点的 map 映射,方便后面快速查找到对应的节点数据;
Map<String, LemonTreeEntity> lemonTreeNodeMap = new HashMap<>();
lemonTreeNodeList.stream().forEach(lemonTreeEntity -> {
String nodeId = lemonTreeEntity.getNodeId();
lemonTreeNodeMap.put(nodeId, lemonTreeEntity);
});
// 结果集;
List<LemonTreeEntity> lemonTreeResult = new ArrayList<>();
// 循环这个有序的集合,组装最终的树状结构的数据;
lemonTreeNodeList.stream().forEach(lemonTreeEntity -> {
String parentNodeId = lemonTreeEntity.getParentNodeId();
if (StringUtils.isEmpty(parentNodeId)) {
// 说明是树的根节点,直接放在集合中;
lemonTreeResult.add(lemonTreeEntity);
} else {
// 其它都是子节点,需要将每个子节点放在对应的父节点的孩子集合中。
LemonTreeEntity parentTreeNodeEntity = lemonTreeNodeMap.get(parentNodeId);
List<LemonTreeEntity> children = parentTreeNodeEntity.getChildren();
if (children == null) {
children = new ArrayList<>();
parentTreeNodeEntity.setChildren(children);
}
children.add(lemonTreeEntity);
}
});
// String json = JSON.toJSONString(lemonTreeResult);
// System.out.println("json = " + json);
return lemonTreeResult;
}
private List<LemonTreeEntity> initLemonTree() {
File file = null;
try {
file = ResourceUtils.getFile("classpath:lemon_tree.json");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
String json = null;
try {
json = FileUtils.readFileToString(file, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("json = " + json);
List<LemonTreeEntity> lemonTreeEntityList = JSON.parseArray(json, LemonTreeEntity.class);
System.out.println("lemonTreeEntityList = " + lemonTreeEntityList);
return lemonTreeEntityList;
}
}
package com.concat.example.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@NoArgsConstructor
public class LemonTreeEntity {
private String nodeId;
private String nodeDesc;
private String parentNodeId;
private List<LemonTreeEntity> children;
}
Web项目其它相关:
package com.concat.example.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class MyResponseEntity<T> implements Serializable {
private String code;
private String message;
private T data;
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.concat.example</groupId>
<artifactId>spring-initialize-template</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-initialize-template</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- 反序列化json字符串 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.14</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>