背景
在開發過程中經常需要把平鋪的數據結構轉為樹形的數據結構,例如多級菜單、組織機構等。
實現方案有很多種。
1、可以使用遞歸查詢,但是這樣數據一多會導致頻繁的多次查詢數據庫,產生很多額外的IO開銷,總體的響應時間會比較慢,一般不會這樣做。
2、可以事先查詢出來所有的數據,再進行遞歸的子節點查找,這是一個可行的方案,只需要查詢一次數據庫,之后的操作利用算法在內存操作,這樣響應時間會有一個很大的提升。
3、這里要說的一種方案和第二種類似,只不過采用了google的guava包下的Multimap這種數據結構,利用它可以一個key對應多個值的特性。
方案實現
引入guava包
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.2.0-jre</version>
</dependency><!-- 這個包可以不要,這里我用來轉json字符串打印出來有用到 -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
</dependency>
樹形VO
@Data
public class TreeVO {private List<TreeVO> children;private int id;private boolean leaf;private String menuName;private int parentId;
}
轉樹示例代碼
public static void main(String[] args) {TreeVO v1 = new TreeVO();v1.setId(10L);v1.setParentId(0L);v1.setMenuName("第一級菜單");TreeVO v2 = new TreeVO();v2.setId(11L);v2.setParentId(10L);v2.setMenuName("第二級菜單1");TreeVO v21 = new TreeVO();v21.setId(12L);v21.setParentId(10L);v21.setMenuName("第二級菜單2");TreeVO v3 = new TreeVO();v3.setId(21L);v3.setParentId(11L);v3.setMenuName("第三級菜單");Multimap<Long,TreeVO> multimap = ArrayListMultimap.create();multimap.put(v1.getParentId(),v1);multimap.put(v2.getParentId(),v2);multimap.put(v21.getParentId(),v21);multimap.put(v3.getParentId(),v3);Iterator<TreeVO> iterator = multimap.values().iterator();while (iterator.hasNext()) {TreeVOmenuNode = iterator.next();// 找直接后代 childrenCollection<TreeVO> children = multimap.get(menuNode.getId());if (children.isEmpty()) {menuNode.setLeaf(true);menuNode.setChildren(null);} else {menuNode.setChildren(children);}}System.out.println(JSON.toJSONString(multimap.get(0L),SerializerFeature.PrettyFormat));}
這里打印出來的結果是
[
?? ?{
?? ??? ?"children":[
?? ??? ??? ?{
?? ??? ??? ??? ?"children":[
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?"id":21,
?? ??? ??? ??? ??? ??? ?"leaf":true,
?? ??? ??? ??? ??? ??? ?"menuName":"第三級菜單",
?? ??? ??? ??? ??? ??? ?"parentId":11
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?],
?? ??? ??? ??? ?"id":11,
?? ??? ??? ??? ?"leaf":false,
?? ??? ??? ??? ?"menuName":"第二級菜單1",
?? ??? ??? ??? ?"parentId":10
?? ??? ??? ?},
?? ??? ??? ?{
?? ??? ??? ??? ?"id":12,
?? ??? ??? ??? ?"leaf":true,
?? ??? ??? ??? ?"menuName":"第二級菜單2",
?? ??? ??? ??? ?"parentId":10
?? ??? ??? ?}
?? ??? ?],
?? ??? ?"id":10,
?? ??? ?"leaf":false,
?? ??? ?"menuName":"第一級菜單",
?? ??? ?"parentId":0
?? ?}
]