# Java树形结构拼接

场景,比如有菜单,一级菜单,二级菜单,三级菜单 子菜单pid等于父菜单id

@Data
class Menu {
    private Integer id;
    private Integer pid;
    private String name;
    private String url;
    private Set<Menu> children = new HashSet<>();
    public boolean isRoot() {
        return this.pid == null || this.pid < 0;
    }
}
1
2
3
4
5
6
7
8
9
10
11

方式一,双重for

    private Set<Menu> menuTree1(Set<Menu> data) {
        Set<Menu> res = new HashSet<>();
        for (Menu menu : data) {
            if (menu.isRoot()) {
                res.add(menu);
            }
            for (Menu item : data) {
                if (menu.getId().equals(item.getPid())) {
                    menu.getChildren().add(item);
                }
            }
        }
        return res;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

方式二,Map实现

    private Set<Menu> menuTree2(Set<Menu> data) {
        Set<Menu> rootSet = data.stream().filter(Menu::isRoot).collect(Collectors.toSet());
        Map<Integer, Menu> menuMap = data.stream().collect(Collectors.toMap(Menu::getId, Function.identity()));
        for (Menu item : data) {
            if (menuMap.containsKey(item.getPid())) {
                Menu Menu = menuMap.get(item.getPid());
                Menu.getChildren().add(item);
            }
        }
        return rootSet;
    }
1
2
3
4
5
6
7
8
9
10
11

方式三,hutool工具类

    private Set<Menu> menuTree2(Set<Menu> data) {
        TreeNodeConfig config = new TreeNodeConfig(); // 配置
        config.setIdKey("id"); // 树形数据中id的属性名
        config.setDeep(3); // 展示目录深度,数据中一共四级目录

        //转换器 1. null 这里表示根节点id (node, tree) node是原来的数据List中的item,tree是要返回的,可以任意定义字段
        List<Tree<String>> list = TreeUtil.build(new ArrayList<>(data), null, config, (node, tree) -> {
            tree.setId(node.getId().toString());
            tree.setName(node.getName());
            tree.setParentId(node.getPid() == null ? null : node.getPid().toString());

            //其他节点
            tree.put("url", node.getUrl());
        });
        //这个是为了展示而已,序列化为对象
        List<Menu> resList = JSONUtil.toList(JSONUtil.toJsonStr(list), Menu.class);
        return new HashSet<>(resList);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

测试数据

Set<Menu> getData() {
    String s = "[{\"id\":101,\"name\":\"系统管理\",\"url\":\"/sys\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":102,\"name\":\"商品管理\",\"url\":\"/goods\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":103,\"name\":\"新闻管理\",\"url\":\"/news\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2001,\"pid\":101,\"name\":\"用户管理\",\"url\":\"/sys/users\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2002,\"pid\":101,\"name\":\"角色管理\",\"url\":\"/sys/roles\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2003,\"pid\":101,\"name\":\"权限管理\",\"url\":\"/sys/permission\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2004,\"pid\":102,\"name\":\"商品列表\",\"url\":\"/goods/goods\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2005,\"pid\":102,\"name\":\"分类列表\",\"url\":\"/goods/categorys\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2006,\"pid\":102,\"name\":\"订单列表\",\"url\":\"/goods/orders\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":2007,\"pid\":103,\"name\":\"新闻列表\",\"url\":\"/news/news\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]},{\"id\":30001,\"pid\":2007,\"name\":\"新闻详情\",\"url\":\"/news/news/detail\",\"status\":1,\"createTime\":1652624114000,\"childPermissions\":[]}]";
    
    Set<Menu> permissionList = new HashSet<>(JSONUtil.toList(s, Menu.class));
    return permissionList;
}

@Test
void tree() {
    Set<Menu> data = getData();
    // 组装树形结构
    Set<Menu> res = menuTree2(data);

    System.out.println(JSONUtil.toJsonStr(res));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

还有一个递归,这里没去测试