java後端操作樹結構

2023-10-23 15:00:43

一、樹結構的三種組裝方式(遞迴.雙層for迴圈,map)

(1)遞迴
普通遞迴方法

 public Result getBmsMenuList(UserSessionVO userSessionInfo) {
        // 查詢頂級節點選單
        List<BmsMenuVO> bmsMenuVOList = bmsMenuDao.selectBmsMenuList(new BmsMenuQueryConditionVO());

        for (BmsMenuVO bmsMenuVO : bmsMenuVOList) {
            getBmsMenuListByRecursion(bmsMenuVO);
        }

        return Result.createWithModels(null, bmsMenuVOList);
    }

 private void getBmsMenuListByRecursion(BmsMenuVO bmsMenuVO) {
        List<BmsMenuVO> bmsMenuVOS = bmsMenuDao.selectBmsMenuList(new BmsMenuQueryConditionVO().setParentId(bmsMenuVO.getId()));
        if (CollectionUtils.isEmpty(bmsMenuVOS)) {
            return;
        }
        bmsMenuVO.setChildBmsMenuList(bmsMenuVOS);
        for (BmsMenuVO menuVO : bmsMenuVOS) {
            getBmsMenuListByRecursion(menuVO);
        }
}

stream流遞迴方法

//獲取父節點
List<TreeSelect> collect = trees.stream().filter(m -> m.getParentId() == 0).map(
    (m) -> {
        m.setChildren(getChildrenList(m, trees));
        return m;
    }
).collect(Collectors.toList());
 
    /**
     * 獲取子節點列表
     * @param tree
     * @param list
     * @return
     */
    public static List<TreeSelect> getChildrenList(TreeSelect tree, List<TreeSelect> list){
        List<TreeSelect> children = list.stream().filter(item -> Objects.equals(item.getParentId(), tree.getId())).map(
                (item) -> {
                    item.setChildren(getChildrenList(item, list));
                    return item;
                }
        ).collect(Collectors.toList());
        return children;
    }

(2)雙層for迴圈

// 查詢主節點
List<BmsMenuVO> bmsMenuVOList = bmsRoleMenuDao.getAllRoleMenuList(condition);

// 拼裝結果
 List<BmsMenuVO> bmsMenuTree = new ArrayList<>();
 for (BmsMenuVO bmsMenuVO : bmsMenuVOList) {
      // 根節點的父Id為null
      if (bmsMenuVO.getParentId() == null) {
                bmsMenuTree.add(bmsMenuVO);
      }
      for (BmsMenuVO menuVO : bmsMenuVOList) {
           if (menuVO.getParentId() != null && menuVO.getParentId().equals(bmsMenuVO.getId())) {
               if (CollectionUtils.isEmpty(bmsMenuVO.getChildBmsMenuList())) {
                        bmsMenuVO.setChildBmsMenuList(new ArrayList<>());
                }
                bmsMenuVO.getChildBmsMenuList().add(menuVO);
           }
       }
  }

  // 返回結果
 return Result.createWithModels(null, bmsMenuTree);

(3)map遍歷

 // 查詢所有節點
 List<BmsMenuVO> bmsMenuVOList = bmsRoleMenuDao.getAllRoleMenuList(condition);
       
 // 拼裝結果     
 List<BmsMenuVO> bmsMenuTree = new ArrayList<>();
 // 用來儲存節點的子元素map
 Map<Long, BmsMenuVO> childBmsMenuMap = new LinkedHashMap<>();
        for (BmsMenuVO menuVO : bmsMenuVOList) {
            childBmsMenuMap.put(menuVO.getId(), menuVO);
        }
        for (Long bmsMenuId : childBmsMenuMap.keySet()) {
            BmsMenuVO menuVO = childBmsMenuMap.get(bmsMenuId);
            Long parentId = menuVO.getParentId();
            if (parentId == null) {
                bmsMenuTree.add(menuVO);
            } else {
                BmsMenuVO parentMenuVO = childBmsMenuMap.get(parentId);
                if (parentMenuVO.getChildBmsMenuList() == null) {
                    parentMenuVO.setChildBmsMenuList(new ArrayList<>());
                }
                parentMenuVO.getChildBmsMenuList().add(menuVO);
          }
} 

2、使用遞迴查詢某個節點所在的樹結構

使用場景:當我們得到一個樹形結構資料時,可能需要在樹形結構上對資料進行篩選,例如通過資料夾(檔案)名稱模糊查詢相關的資料夾並展現其父級。

缺點:需要查詢出完整的樹形結構才能用作篩選,在資料量非常龐大的時候並不適用。

@Data
public class TreeDto {

   private String id;
   private String name;
   private List<TreeDto> subsetTreeDtoList;



   public TreeDto(String id,String name,List<TreeDto> subsetTreeDtoList){
       this.id = id;
       this.name = name;
       this.subsetTreeDtoList = subsetTreeDtoList;
   }
}

篩選

  /**
    * 樹形篩選查詢
    * @param treeDtoList 樹形集合
    * @param idList 篩選條件(可以是其他條件)
    * @return 包含的節點資料
    */
   public static List<TreeDto> screenTree(List<TreeDto> treeDtoList, List<String> idList){
       //最後返回的篩選完成的集合
       List<TreeDto> screeningOfCompleteList = new ArrayList<>();
       if (listIsNotEmpty(treeDtoList) && listIsNotEmpty(idList)){
           for (TreeDto treeDto : treeDtoList){
               List<TreeDto> subsetList = treeDto.getSubsetTreeDtoList();
               //遞迴篩選完成後的返回的需要新增的資料
               TreeDto addTreeDto = getSubsetPmsPlanPo(treeDto,subsetList,idList);
               if (isNotEmpty(addTreeDto)){
                   screeningOfCompleteList.add(addTreeDto);
               }
           }
           return screeningOfCompleteList;
       }
       return null;
   }


   /**
    * 篩選符合的集合並返回
    * @param treeDto 樹形類
    * @param subsetTreeDtoList 子集集合
    * @param idList 篩選條件
    * @return 篩選成功的類
    */
   public static TreeDto getSubsetPmsPlanPo(TreeDto treeDto,List<TreeDto> subsetTreeDtoList,List<String> idList){
       //作為篩選條件的判斷值
       String id = treeDto.getId();
       //有子集時繼續向下尋找
       if (listIsNotEmpty(subsetTreeDtoList)){
           List<TreeDto> addTreeDtoList = new ArrayList<>();
           for (TreeDto subsetTreeDto : subsetTreeDtoList){
               List<TreeDto> subsetList = subsetTreeDto.getSubsetTreeDtoList();
               TreeDto newTreeDto = getSubsetPmsPlanPo(subsetTreeDto,subsetList,idList);
               //當子集篩選完不為空時新增
               if (isNotEmpty(newTreeDto)){
                   addTreeDtoList.add(newTreeDto);
               }
           }
           //子集滿足條件篩選時集合不為空時,替換物件集合內容並返回當前物件
           if (listIsNotEmpty(addTreeDtoList)){
               treeDto.setSubsetTreeDtoList(addTreeDtoList);
               return treeDto;
               //當前物件子集物件不滿足條件時,判斷當前物件自己是否滿足篩選條件,滿足設定子集集合為空,並返回當前物件
           }else if (listIsEmpty(addTreeDtoList) && idList.contains(id)){
               treeDto.setSubsetTreeDtoList(null);
               return treeDto;
           }else {
               //未滿足篩選條件直接返回null
               return null;
           }
       }else {
           //無子集時判斷當前物件是否滿足篩選條件
           if (idList.contains(id)){
               return treeDto;
           }else {
               return null;
           }
       }
   }

   /**
    * 判斷集合為空
    * @param list 需要判斷的集合
    * @return 集合為空時返回 true
    */
   public static boolean listIsEmpty(Collection list){
       return  (null == list || list.size() == 0);
   }

   /**
    * 判斷集合非空
    * @param list 需要判斷的集合
    * @return 集合非空時返回 true
    */
   public static boolean listIsNotEmpty(Collection list){
       return !listIsEmpty(list);
   }

   /**
    * 判斷物件為null或空時
    * @param object 物件
    * @return 物件為空或null時返回 true
    */
   public static boolean isEmpty(Object object) {
       if (object == null) {
           return (true);
       }
       if ("".equals(object)) {
           return (true);
       }
       if ("null".equals(object)) {
           return (true);
       }
       return (false);
   }

   /**
    * 判斷物件非空
    * @param object 物件
    * @return 物件為非空時返回 true
    */
   public static boolean isNotEmpty(Object object) {
       if (object != null && !object.equals("") && !object.equals("null")) {
           return (true);
       }
       return (false);
   }

   public static void main(String[] args) {
       TreeDto treeDto1 = new TreeDto("1","A",new ArrayList<TreeDto>());
       TreeDto treeDto1_1 = new TreeDto("1.1","A-A",new ArrayList<TreeDto>());
       TreeDto treeDto1_2 = new TreeDto("1.2","A-B",new ArrayList<TreeDto>());
       TreeDto treeDto1_3 = new TreeDto("1.3","A-C",new ArrayList<TreeDto>());
       treeDto1.getSubsetTreeDtoList().add(treeDto1_1);
       treeDto1.getSubsetTreeDtoList().add(treeDto1_2);
       treeDto1.getSubsetTreeDtoList().add(treeDto1_3);

       TreeDto treeDto2 = new TreeDto("2","B",new ArrayList<TreeDto>());
       TreeDto treeDto2_1 = new TreeDto("2.1","B-A",new ArrayList<TreeDto>());
       TreeDto treeDto2_2 = new TreeDto("2.2","B-B",new ArrayList<TreeDto>());
       TreeDto treeDto2_3 = new TreeDto("2.3","B-C",new ArrayList<TreeDto>());
       TreeDto treeDto2_3_1 = new TreeDto("2.3.1","B-C-A",null);
       treeDto2.getSubsetTreeDtoList().add(treeDto2_1);
       treeDto2.getSubsetTreeDtoList().add(treeDto2_2);
       treeDto2.getSubsetTreeDtoList().add(treeDto2_3);
       treeDto2.getSubsetTreeDtoList().get(2).getSubsetTreeDtoList().add(treeDto2_3_1);

       String[] array = {"1.3","2.2","2.3.1"};
       List<String> idList = Arrays.asList(array);
       List<TreeDto> treeDtoList = new ArrayList<>();
       treeDtoList.add(treeDto1);
       treeDtoList.add(treeDto2);
       System.out.println(JSON.toJSONString(screenTree(treeDtoList,idList)));
   }
}

返回結果為