Java常用代码合集2

1/31/2024 树形工具类

# 树形工具类合集

# 方法一

适用多级菜单、多级评论、多级部门、多级分类的统一工具类

# 树形结构类

/**
 * @Description: 树形结构类
 */
public interface ITreeNode<T> {
  
    /**
     * @return 获取当前元素Id
     */
    Object getId();

    /**
     * @return 获取父元素Id
     */
    Object getParentId();

    /**
     * @return 获取当前元素的 children 属性
     */
    List<T> getChildren();

    /**
     * ( 如果数据库设计有tree_path字段可覆盖此方法来生成tree_path路径 )
     *
     * @return 获取树路径
     */
    default Object getTreePath() { return ""; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# TreeNodeUtil

/**
 * @Description: 树形结构工具类
 */
public class TreeNodeUtil {

    private static final Logger log = LoggerFactory.getLogger(TreeNodeUtil.class);

    public static final List<Object> IDS = Collections.singletonList(0L);

    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList) {
        return buildTree(dataList, IDS, (data) -> data, (item) -> true);
    }

    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList, Function<T, T> map) {
        return buildTree(dataList, IDS, map, (item) -> true);
    }
    
    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList, Function<T, T> map, Predicate<T> filter) {
        return buildTree(dataList, IDS, map, filter);
    }

    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids) {
        return buildTree(dataList, ids, (data) -> data, (item) -> true);
    }

    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids, Function<T, T> map) {
        return buildTree(dataList, ids, map, (item) -> true);
    }

    /**
     * 数据集合构建成树形结构
     * 如果最开始的 ids 不在 dataList 中,不会进行任何处理 )
     * @param dataList 数据集合
     * @param ids      父元素的 Id 集合
     * @param map      调用者提供 Function<T, T> 由调用着决定数据最终呈现形势
     * @param filter   调用者提供 Predicate<T> false 表示过滤 ( 注: 如果将父元素过滤掉等于剪枝 )
     * @param <T>      extends ITreeNode
     * @return
     */
    public static <T extends ITreeNode> List<T> buildTree(List<T> dataList, List<Object> ids, Function<T, T> map, Predicate<T> filter) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        // 将数据分为 父子结构
        Map<Boolean, List<T>> nodeMap = dataList.stream()
                .filter(filter)
                .collect(Collectors.partitioningBy(item -> ids.contains(item.getParentId())));
        
        List<T> parent = nodeMap.getOrDefault(true, Collections.emptyList());
        List<T> children = nodeMap.getOrDefault(false, Collections.emptyList());
    
        if (parent.isEmpty()) {
            return children;
        }
    
        List<Object> nextIds = new ArrayList<>(dataList.size());
        List<T> collectParent = parent.stream().map(map).collect(Collectors.toList());
        for (T parentItem : collectParent) {
            if (nextIds.size() == children.size()) {
                break;
            }
            nextIds.addAll(children.stream()
                    .filter(childrenItem -> parentItem.getId().equals(childrenItem.getParentId()))
                    .peek(parentItem.getChildren()::add)
                    .map(T::getParentId)
                    .collect(Collectors.toList()));
        }
        buildTree(children, nextIds, map, filter);
        return parent;
	}


    /**
     * 生成路径 treePath 路径
     *
     * @param currentId 当前元素的 id
     * @param getById   用户返回一个 T
     * @param <T>
     * @return
     */
    public static <T extends ITreeNode> String generateTreePath(Serializable currentId, Function<Serializable, T> getById) {
        if (SystemConstants.ROOT_NODE_ID.equals(currentId)) {
            return currentId.toString();
        } else {
            T byId = getById.apply(currentId);
            if (!ObjectUtils.isEmpty(byId)) {
                String parentTreePath = generateTreePath(byId.getParentId(), getById);
                return parentTreePath + "," + byId.getId();
            }
        }
    	return "";
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

# 使用示例

/**
 * @Description: 测试树形工具类
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@AllArgsConstructor
public class TestChildren implements ITreeNode<TestChildren> {

    private Long id;

    private String name;

    private String treePath;

    private Long parentId;

    public TestChildren(Long id, String name, String treePath, Long parentId) {
        this.id = id;
        this.name = name;
        this.treePath = treePath;
        this.parentId = parentId;
    }

    @TableField(exist = false)
    private List<TestChildren> children = new ArrayList<>();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static void main(String[] args) {
    List<TestChildren> testChildren = new ArrayList<>();
    testChildren.add(new TestChildren(1L, "父元素", "", 0L));
    testChildren.add(new TestChildren(2L, "子元素1", "1", 1L));
    testChildren.add(new TestChildren(3L, "子元素2", "1", 1L));
    testChildren.add(new TestChildren(4L, "子元素2的孙子元素", "1,3", 3L));

    testChildren = TreeNodeUtil.buildTree(testChildren);

    System.out.println(JSONUtil.toJsonStr(Result.success(testChildren)));

    // 对 3L 进行剪枝,对 1L 进行修改
    testChildren = TreeNodeUtil.buildTree(testChildren, (item) -> {
        if (item.getId().equals(1L)) {
            item.setName("更改了 Id 为 1L 的数据名称");
        }
        return item;
    }, (item) -> item.getId().equals(3L));

    System.out.println(JSONUtil.toJsonStr(Result.success(testChildren)));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 方法二

/**
 * 伪代码
 */
public class QcsbController {
    
    private static Map<Integer,List<QcsbModel>> map = new HashMap<>();

    /**
     * 获取属性结构下拉树
     *
     * @return {@link List}<{@link Tree}<{@link Integer}>>
     */
    @GetMapping("/tree")
    public List<Tree<Integer>> equipmentTree(){
        List<QcsbModel> data = qcsbService.getSelectTreeData();
        // 顶级节点下的所有子级
        List<QcsbModel> collect = data.stream().filter(x -> x.getPid().equals(0)).collect(Collectors.toList());
        for (QcsbModel model : data) {
                if (!model.getPid().equals(0)){
                    if (!map.containsKey(model.getPid())){
                        List<QcsbModel> list1 = new ArrayList<>();
                        list1.add(model);
                        map.put(model.getPid(),list1);
                    }else {
                        List<QcsbModel> list2 = map.get(model.getPid());
                        list2.add(model);
                        map.put(model.getPid(),list2);
                    }
                }
        }
        List<QcsbModel> collect2 = collect.stream()
                    .peek(this::getL).collect(Collectors.toList());
        log.info("最终数据{}", JSONUtil.toJsonStr(collect2));
        return equipmentTreeList;
    }

    public void getL(QcsbModel model){
        if (map.containsKey(model.getQcsbId())){
            model.setChildren(map.get(model.getQcsbId()));
            for (int i = 0; i < model.getChildren().size(); i++) {
                getL(model.getChildren().get(i));
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 方法三

方法3未完全测试,仅供参考

public interface SysMenuService extends IService<SysMenu> {
    List<SysMenu> getMenuTree();
}

@Service("sysMenuService")
public class SysMenuServiceImpl extends ServiceImpl<SysMenuDao, SysMenu> implements SysMenuService {

    @Autowired
    private SysMenuDao sysMenuDao ;
    @Override
    public List<SysMenu> getMenuTree() {
        //查询出所有菜单
        List<SysMenu> sysMenus = sysMenuDao.selectList(null);
        //2、组装成树形结构
        //2.1)、找到所有的一级菜单
        List<SysMenu> level1Menus = sysMenus.stream().filter(sysMenu ->
                                                             sysMenu.getPid() == 0
                                                            ).map((menu) -> {
            menu.setChildren(getChildrens(menu, sysMenus));
            return menu;
            // 排序
        }).sorted((menu1, menu2) -> {
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());
        return level1Menus;
    }

    //递归查找所有菜单的子菜单,主要就是用了这个递归查询。
    private List<SysMenu> getChildrens(SysMenu root, List<SysMenu> all) {

        List<SysMenu> children = all.stream().filter(sysMenu -> {
            return sysMenu.getPid() == root.getId();
        }).map(sysMenu -> {
            //1、找到子菜单
            sysMenu.setChildren(getChildrens(sysMenu, all));
            return sysMenu;
        }).sorted((menu1, menu2) -> {
            //2、菜单的排序
            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());
        return children;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 方法四

方法 4 来源 Hutool 工具类

public List<Tree<Integer>> equipmentTree(){
  List<Model> data = service.getSelectTreeData();
  //配置
  TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
  // 自定义属性名
  treeNodeConfig.setIdKey("Id");
  treeNodeConfig.setParentIdKey("pid");
  //转换器
  equipmentTreeList = TreeUtil.build(data, CodeConstants.ZERO, treeNodeConfig,
    (treeNode, tree) -> {
        tree.setId(treeNode.getId());
        tree.setParentId(treeNode.getPid());
        tree.setName(treeNode.getName());
        });
  return equipmentTreeList;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16