引言:
在日常开发中,我们经常会遇到需要将数据库中返回的数据转成树形结构的数据返回,或者需要对转为树结构后的数据绑定层级关系再返回,比如需要统计当前节点下有多少个节点等,因此我们需要封装一个ListToTree的工具类和学会如何通过深度优先遍历数据。
数据准备:
先简单准备一下具有父子关系的数据。
java
package data;
public class OrgData {
private String id;
private String pId;
private String orgName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getpId() {
return pId;
}
public void setpId(String pId) {
this.pId = pId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
}
java
package data;
import java.util.ArrayList;
import java.util.List;
public class SingData {
public static List<OrgData> getData() {
OrgData orgData1 = new OrgData();
orgData1.setId("1");
orgData1.setpId("root");
orgData1.setOrgName("根节点A");
OrgData orgData2 = new OrgData();
orgData2.setId("2");
orgData2.setpId("root");
orgData2.setOrgName("根节点B");
OrgData orgData3 = new OrgData();
orgData3.setId("3");
orgData3.setpId("1");
orgData3.setOrgName("A目录");
OrgData orgData4 = new OrgData();
orgData4.setId("4");
orgData4.setpId("2");
orgData4.setOrgName("B目录");
List<OrgData> list = new ArrayList<>();
list.add(orgData1);
list.add(orgData2);
list.add(orgData3);
list.add(orgData4);
return list;
}
}
正常情况下,我们都会选择封装一个将List转换为Tree的工具类,并且通过链式顺序LinkedList存储来遍历Tree数据,这里使用stream来实现,如下:
java
package data;
import java.util.List;
public class TreeBean {
private String treeId;
private String treePid;
private String orgName;
private Integer flag = 0;
private List<TreeBean> children;
private OrgData orgData;
public String getTreeId() {
return treeId;
}
public void setTreeId(String treeId) {
this.treeId = treeId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getTreePid() {
return treePid;
}
public void setTreePid(String treePid) {
this.treePid = treePid;
}
public List<TreeBean> getChildren() {
return children;
}
public void setChildren(List<TreeBean> children) {
this.children = children;
}
public OrgData getOrgData() {
return orgData;
}
public void setOrgData(OrgData orgData) {
this.orgData = orgData;
}
public Integer getFlag() {
return flag;
}
public void setFlag(Integer flag) {
this.flag = flag;
}
@Override
public String toString() {
return "TreeBean{" +
"treeId='" + treeId + '\'' +
", treePid='" + treePid + '\'' +
", orgName='" + orgName + '\'' +
", children=" + children +
'}';
}
}
执行结果:
620fc2d13f944d61888c2f0af8198ad2.png
对tree数据进行遍历,通过链表的形式:
java
import data.OrgData;
import data.SingData;
import data.TreeBean;
import utils.ListToTreeUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
// 模拟查询数据
List<OrgData> orgDataList = SingData.getData();
// 构建tree数据
List<TreeBean> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
// 使用LinkedList实现链式队列,实现深度遍历
LinkedList<TreeBean> stack = new LinkedList<>();
stack.addAll(treeBean);
while (!stack.isEmpty()) {
// 从栈顶开始访问
TreeBean pop = stack.peek();
// Flag=1表示已经遍历过一次且该节点存在子节点
if (pop.getFlag() == 1) {
OrgData orgData =pop.getOrgData();
List<TreeBean> children = pop.getChildren();
// 获取子节点的节点名称,也可以进行其他的操作
List<String> collect = children.stream().map(TreeBean::getOrgName).collect(Collectors.toList());
StringBuilder builder = new StringBuilder();
for (String s : collect) {
builder.append(s);
builder.append(">");
}
pop.setOrgName(builder.toString());
orgData.setOrgName(pop.getOrgName());
// pop出栈,当前节点已经统计完,出栈获取下一个栈顶peek
stack.pop();
} else {
// flag为0表示未遍历,判断是否已经遍历到叶子节点(最底部)
if (pop.getChildren()!= null && !pop.getChildren().isEmpty()) {
// 非叶子节点
pop.setFlag(1);
List<TreeBean> children = pop.getChildren();
for (TreeBean child : children) {
// 将叶子节点入栈,放到栈顶,实现深度遍历,next
stack.push(child);
}
} else {
// 叶子节点直接出栈即可,cnt为本身
stack.pop();
}
}
}
// 遍历最终的数据
for (OrgData orgData : orgDataList) {
System.out.println(orgData.toString());
}
}
}
但是现在有一个问题,当我们响应的数据从OrgData换到其他类型的时候,这时候就需要封装成一个泛型类使得我们的tree数据生成类变成一个通用的,完整代码如下:
完整代码:JAVA将List转为Tree和深度优先遍历
java
package data;
import java.util.List;
public interface Tree<T> {
String getTreeId();
String getTreePid();
void setChild(List<T> list);
}
java
package data;
import java.util.List;
/**
* 实现Tree<OrgData>并定义响应类型为本身
* 定义转为树的参数返回
*/
public class OrgData implements Tree<OrgData>{
private String id;
private String pId;
private String orgName;
// 转成树需要的参数
private Integer flag = 0;
private List<OrgData> child;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getpId() {
return pId;
}
public void setpId(String pId) {
this.pId = pId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public Integer getFlag() {
return flag;
}
public void setFlag(Integer flag) {
this.flag = flag;
}
public List<OrgData> getChild() {
return child;
}
@Override
public String toString() {
return "OrgData{" +
"id='" + id + '\'' +
", pId='" + pId + '\'' +
", orgName='" + orgName + '\'' +
'}';
}
@Override
public String getTreeId() {
return id;
}
@Override
public String getTreePid() {
return pId;
}
@Override
public void setChild(List<OrgData> list) {
this.child = list;
}
}
ListToTree方法
java
package utils;
import data.OrgData;
import data.SingData;
import data.Tree;
import data.TreeBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 将List转为Tree
* <T extends Tree>告诉编译器有Tree的get方法
*
*/
public class ListToTreeUtil {
public static <T extends Tree> List<T> toTree(List<T> list, String root) {
// 当根节点为null时,定义一个初始值,防止null
String treeRoot = "treeRoot";
String finalRoot = root;
if (list.isEmpty()) {
return new ArrayList<>();
}
// 构建Map数据
// 根据pid分组,获取所有的子节点集合
Map<String, List<T>> childMap =
list.stream()
.collect(Collectors.groupingBy(item -> {
String treePid = item.getTreePid();
if (treePid == null) {
treePid = treeRoot;
}
return treePid;
}));
return list.stream().peek(
data -> {
List<T> children = childMap.get(data.getTreeId());
if (children != null && !children.isEmpty()) {
data.setChild(children);
}
})
.filter(data -> data.getTreePid().equals(finalRoot))
.collect(Collectors.toList());
}
}
深度优先遍历
java
import data.OrgData;
import data.SingData;
import data.TreeBean;
import utils.ListToTreeUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
// 模拟查询数据
List<OrgData> orgDataList = SingData.getData();
// 构建tree数据
List<OrgData> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
// 使用LinkedList实现链式队列,实现深度遍历
LinkedList<OrgData> stack = new LinkedList<>();
stack.addAll(treeBean);
while (!stack.isEmpty()) {
// 从栈顶开始访问
OrgData pop = stack.peek();
// Flag=1表示已经遍历过一次且该节点存在子节点
if (pop.getFlag() == 1) {
List<OrgData> children = pop.getChild();
// 获取子节点的节点名称,也可以进行其他的操作
List<String> collect = children.stream().map(OrgData::getOrgName).collect(Collectors.toList());
StringBuilder builder = new StringBuilder();
for (String s : collect) {
builder.append(s);
builder.append(">");
}
pop.setOrgName(builder.toString());
// pop出栈,当前节点已经统计完,出栈获取下一个栈顶peek
stack.pop();
} else {
// flag为0表示未遍历,判断是否已经遍历到叶子节点(最底部)
if (pop.getChild()!= null && !pop.getChild().isEmpty()) {
// 非叶子节点
pop.setFlag(1);
List<OrgData> children = pop.getChild();
for (OrgData child : children) {
// 将叶子节点入栈,放到栈顶,实现深度遍历,next
stack.push(child);
}
} else {
// 叶子节点直接出栈即可,cnt为本身
stack.pop();
}
}
}
// 遍历最终的数据
for (OrgData orgData : orgDataList) {
System.out.println(orgData.toString());
}
}
}