需求场景
有下面一张区域表,典型的树形结构设计。
现前端需要后端返回树形数据结构用于构造展示树。
本篇文章我们就来介绍一下在这种场景下后端构建树形数据结构,也就是通过list转tree的4种写法。
代码实战
- 首先我们根据数据库结构创建实体对象
/** * 区域平台 * @author:Jam */ @Data public class Platform { private String id; private String parentId; private String name; private String platformCode; private List<Platform> children; public Platform(String id, String platformCode,String parentId, String name) { this.id = id; this.parentId = parentId; this.name = name; this.platformCode = platformCode; } }
- 为了便于演示我们就不连接数据库,而是直接使用Junit5的
@BeforeEach
注解初始化一份结构数据。
public class PlatformTest { private final List<Platform> platformList = Lists.newArrayList(); private ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach private void init(){ Platform platform0 = new Platform("1","001","0","集团"); Platform platform1 = new Platform("2","QYPT001","1","销委会"); Platform platform2 = new Platform("3","QYPT002","2","吉龙大区"); Platform platform3 = new Platform("4","QYPT003","2","江苏大区"); Platform platform4 = new Platform("5","QYPT004","4","南京分区"); Platform platform5 = new Platform("6","QYPT005","1","教育BG"); Platform platform6 = new Platform("7","QYPT006","6","华南大区"); Platform platform7 = new Platform("8","QYPT007","6","华东大区"); platformList.add(platform0); platformList.add(platform1); platformList.add(platform2); platformList.add(platform3); platformList.add(platform4); platformList.add(platform5); platformList.add(platform6); platformList.add(platform7); } }
最无节操的写法
这种写法毫无节操可言,全部通过数据库递归查询。
首先查到根节点,parent_id = 0
通过根节点id获取到所有一级节点,parent_id = 1
递归获取所有节点的子节点,然后调用setChildren()方法组装数据结构。
这种写法我就不展示了,辣眼睛。都2021年了我见过不止一次在项目中出现这种写法。
双重循环
这种写法比较简单,也是比较容易想到的。通过双重循环确定父子节点的关系。
@SneakyThrows @Test public void test1(){ System.out.println(platformList.size()); List<Platform> result = Lists.newArrayList(); for (Platform platform : platformList) { //获取根节点 if(platform.getParentId().equals("0")){ result.add(platform); } for(Platform child : platformList){ if(child.getParentId().equals(platform.getId())){ platform.addChild(child); } } } System.out.println(objectMapper.writeValueAsString(result)); }
同时需要给Platform添加一个addChild()
的方法。
public void addChild(Platform platform){ if(children == null){ children = new ArrayList<>(); } children.add(platform); }
双重遍历
第一次遍历借助hashmap存储父节点与子节点的关系,第二次遍历设置子节点,由于map中已经维护好了对应关系所以只需要从map取即可。
@SneakyThrows @Test public void test2(){ Map<String, List<Platform>> platformMap = new HashMap<>(); platformList.forEach(platform -> { List<Platform> children = platformMap.getOrDefault(platform.getParentId(), new ArrayList<>()); children.add(platform); platformMap.put(platform.getParentId(),children); }); platformList.forEach(platform -> platform.setChildren(platformMap.get(platform.getId()))); List<Platform> result = platformList.stream().filter(v -> v.getParentId().equals("0")).collect(Collectors.toList()); System.out.println(objectMapper.writeValueAsString(result)); }
Stream 分组
@SneakyThrows @Test public void test4(){ Map<String, List<Platform>> groupMap = platformList.stream().collect(Collectors.groupingBy(Platform::getParentId)); platformList.forEach(platform -> platform.setChildren(groupMap.get(platform.getId()))); List<Platform> collect = platformList.stream() .filter(platform -> platform.getParentId().equals("0")).collect(Collectors.toList()); System.out.println(objectMapper.writeValueAsString(collect)); }
此处主要通过Collectors.groupingBy(Platform::getParentId)方法对platformList按照parentId进行分组,分组后父节点相同的都放一起了。
然后再循环platformList,给其设置children属性。
执行完成后已经形成了多颗树,最后我们再通过filter()方法挑选出根节点的那颗树即可。