需求場景
有下面一張區域表,典型的樹形結構設計。
現前端需要後端傳回樹形資料結構用于構造展示樹。
本篇文章我們就來介紹一下在這種場景下後端建構樹形資料結構,也就是通過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()方法挑選出根節點的那顆樹即可。