“孔乙己說:回字有四種寫法。
飄乙己也說,list轉tree也有4種寫法,你用的是哪種?
”
需求場景
有下面一張區域表,典型的樹形結構設計。
現前端需要後端返回樹形數據結構用於構造展示樹。
本篇文章我們就來介紹一下在這種場景下後端構建樹形數據結構,也就是通過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()
方法挑選出根節點的那顆樹即可。
往期精選:
SpringBoot 如何進行參數校驗?老鳥們都是這麼玩的!
更多精彩,點擊關注公衆號
本文分享自微信公衆號 - JAVA日知錄(javadaily)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。