欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Hutools 构建树结构

Hutools 构建树结构

2025/1/22 14:11:21 来源:https://blog.csdn.net/qq_45900010/article/details/145287714  浏览:    关键词:Hutools 构建树结构

详细介绍

Hutools官网

我们平时在进行业务开发时,经常会碰到需要构建树形结构的业务,比如常见的区域树结构

└── 省└── 市└── 区

这类数据的特点就是每条数据有明确的父节点和子节点,如果没有父节点或者子节点的话,没有父节点的数据为树的顶级节点,如果没有子节点的话为树的底级节点。

示例数据表及数据

CREATE TABLE area (area_id BIGINT NOT NULL AUTO_INCREMENT,parent_area_id BIGINT,area_name VARCHAR(255) NOT NULL,PRIMARY KEY (area_id)
);-- 插入省份
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (1, NULL, '北京市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (2, NULL, '上海市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (3, NULL, '广东省');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (4, NULL, '浙江省');-- 插入北京市的市区
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (101, 1, '东城区');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (102, 1, '西城区');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (103, 1, '朝阳区');-- 插入上海市的市区
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (201, 2, '黄浦区');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (202, 2, '徐汇区');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (203, 2, '长宁区');-- 插入广东省的市区
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (301, 3, '广州市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (302, 3, '深圳市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (303, 3, '珠海市');-- 插入浙江省的市区
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (401, 4, '杭州市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (402, 4, '宁波市');
INSERT INTO area (area_id, parent_area_id, area_name) VALUES (403, 4, '温州市');

使用hutools 的TreeUtil工具类可以非常方便的构建一个树形结构。

首先,springboot应用引入需要的hutools的依赖

        <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.26</version></dependency>

然后处理查询出来的需构建数据

@RestController
@RequestMapping("/area")
public class AreaController {@Autowiredprivate IAreaService areaService;@GetMapping("/getAreaTree")public List<Tree<Long>> getAreaTree(){List<Area> areaList = areaService.list();List<Tree<Long>> trees = buildAreaTree(areaList);return trees;}private List<Tree<Long>> buildAreaTree(List<Area> list){TreeNodeConfig config = new TreeNodeConfig();config.setIdKey("id");config.setParentIdKey("pid");List<Tree<Long>> treeList = TreeUtil.build(list, 0L, config, (node, tree) -> {tree.setId(node.getAreaId());tree.setParentId(node.getParentAreaId() == null ? 0L : node.getParentAreaId());tree.setName(node.getAreaName());
//            tree.putExtra("code", node.getAreaCode());  putExtra方法可以在树结构的节点里面加入其他字段});return treeList;}
}

关键代码解释

buildAreaTree()方法这里面主要是使用了TreeUtil.build()方法将区域areaList转化为树形结构。(将一个list转化为tree)

方法功能

  • 方法名buildAreaTree
  • 参数List<Area> list,传入的区域列表,类型为 Area
  • 返回值List<Tree<Long>>,返回构建好的树形结构列表,每个节点类型为 Tree<Long>

主要逻辑

  1. 配置树节点 (TreeNodeConfig)
    • 创建 TreeNodeConfig 对象 config,用于配置树节点属性的映射关系。
    • config.setIdKey("id"):设置节点的唯一标识字段名为 "id"
    • config.setParentIdKey("pid"):设置父节点标识字段名为 "pid"
  2. 构建树 (TreeUtil.build)
    • 使用工具类 TreeUtil.build 方法构建树形结构。
    • list:传入的区域列表,包含所有节点。
    • 0L:根节点的父节点 ID,通常为 0null
    • config:使用前面设置好的 TreeNodeConfig 配置。
    • (node, tree):构建树的回调函数,用于设置每个节点的属性。
      • node.getAreaId():获取当前节点的 areaId
      • node.getParentAreaId():获取当前节点的 parentAreaId,如果为 null,则设置为 0L
      • node.getAreaName():获取当前节点的 areaName
  3. 设置节点属性
    • tree.setId(node.getAreaId()):设置树节点的 ID。
    • tree.setParentId(...):设置树节点的父 ID。
    • tree.setName(node.getAreaName()):设置树节点的名称。
  4. 扩展字段 (putExtra)
    • 注释掉的 putExtra 方法说明了可以为树节点添加额外的字段,比如 areaCode

返回示例节选

[{"id": 4,"pid": 0,"name": "浙江省","children": [{"id": 401,"pid": 4,"name": "杭州市","children": [{"id": 40101,"pid": 401,"name": "西湖区"},{"id": 40102,"pid": 401,"name": "上城区"},{"id": 40103,"pid": 401,"name": "下城区"}]},{"id": 403,"pid": 4,"name": "温州市"}]}
]

这样就成功构成了一个树形结构。

测试代码gitee链接

数据库用到的数据项目中model.sql中都有,自己测试的时候导入到自己的数据库中。

多说一点

有时候在构造一个树结构的时候,数据的来源并非是一张表中的数据,可能涉及到多张表。比如构造一个有关区域和地铁站的树结构,树结构中有区域,然后区域下面是地铁站。(地铁站跟区域肯定是有关联的,每一条地铁站的数据中都存有areaId(area表的主键))

这个时候就要将区域和地铁站融合到一个List中,然后使用TreeUtil,怎么融合呢?

条件:

  1. 比如现在我们有区域的List areaList
  2. 地铁站的List stationList
  3. 新建一个实体类ItemDTO
public class ItemDTO {private Long id;private Long pid;private String name; // 区域名或地铁站名private String otherField;
}

操作:

  1. 将stationList处理成一个map,key为areaId,value为stationList
  2. 新建 List ItemDTOList = new ArrayList;
  3. 遍历areaList,将Area对象的对应字段处理为ItemDTO的id、pid、name;(areaId->id, parentId->pid, areaName->name)
  4. 再次遍历areaList,获取areaIdAndStationMap中的station,将station对应的字段处理为ItemDTO的id、pid、name
  5. 然后使用TreeUtil构建树结构

示例代码:

public void buildTree() {List<Area> areaList = new ArrayList<>();List<Station> stationList = new ArrayList<>();List<ItemDTO> itemDTOList = new ArrayList<>();Map<Long, List<Station>> areaIdAndStationMap = stationList.stream().collect(Collectors.groupingBy(Station::getAreaId));for (Area area : areaList) {ItemDTO itemDTO = new ItemDTO();itemDTO.setId(area.getAreaId());itemDTO.setPid(area.getParentId());itemDTO.setName(area.getAreaName());itemDTOList.add(itemDTO);}for (Area area : areaList) {List<Station> stations = areaIdAndStationMap.get(area.getAreaId());for (Station station : stations) {ItemDTO itemDTO = new ItemDTO();itemDTO.setId(station.getStationId());itemDTO.setPid(station.getAreaId());itemDTO.setName(station.getStationName());itemDTOList.add(itemDTO);}}// 之后,itemDTO作为参数使用TreeUtil构造树结构
}

以此类推,不管是几个有关系的实体类,都将他们处理到一个List中,就能正确的构造树结构

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com