Java中如何返回树状数据结构?

一、如何创建树状数据结构?

1、创建数据库表 City

DROP TABLE IF EXISTS `city`;
CREATE TABLE `city` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) DEFAULT NULL COMMENT '城市名',
  `name_en` varchar(255) DEFAULT NULL COMMENT '城市英文名',
  `parent_id` int(11) DEFAULT NULL COMMENT '父节点ID',
  `del` int(1) NOT NULL DEFAULT '0' COMMENT '假删除 0-未删除 1-已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO `city` VALUES ('1', '江西省', 'JiangXi', '0', '0');
INSERT INTO `city` VALUES ('2', '赣州市', 'GanZhou', '1', '0');
INSERT INTO `city` VALUES ('3', '信丰县', 'XinFeng', '2', '0');
INSERT INTO `city` VALUES ('4', '南昌市', 'NanChang', '1', '0');
INSERT INTO `city` VALUES ('5', '兴国县', 'XinGuo', '2', '0');

2、创建相应的实体类 City

@Entity
@Table(name = "city")
@Data
public class City implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private String nameEn;

    private Integer parentId;

    @Column(insertable = false)
    private Integer del;
}

3、创建相应的 CityRepository

  • 其中继承 JpaSpecificationExecutor 是为了添加查询筛选条件
public interface CityRepository extends JpaRepository<City, Integer>, JpaSpecificationExecutor<City> {
}

4、创建相应的 CityService

public interface CityService {

    /**
     * 返回树状结构数据
     * @return
     */
    List<CityTree> buildTree();
}
  • 其中用到的 CityTree 展示类
@Data
@EqualsAndHashCode
public class CityTree extends TreeNode {

    private String name;

    private String nameEn;

    public CityTree(){

    }

}
  • 继承所用到的 TreeNode
@Data
public class TreeNode implements Serializable {

    private static final long serialVersionUID = 8772115911922451037L;
    protected Object id;
    protected Object parentId;
    protected List<TreeNode> children = new ArrayList<TreeNode>();

    public void add(TreeNode node) {
        children.add(node);
    }
}

5、创建相应的 CityServiceImpl 实现类

@Service
public class CityServiceImpl implements CityService {

    @Autowired
    private CityRepository cityRepository;

    @Override
    public List<CityTree> buildTree() {
        //添加筛选条件
        Specification<City> specification = (Specification<City>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("del").as(Integer.class), 0));
            Predicate[] pre = new Predicate[predicates.size()];
            return criteriaQuery.where(predicates.toArray(pre)).getRestriction();
        };
        List<City> cityList = cityRepository.findAll(specification);
        // cityList 为查询到的结果集, 0 代表的是根节点
        return CityTreeUtil.buildCityTree(cityList, 0);
    }
}
  • 其中用到的 CityTreeUtil 工具类
@UtilityClass
public class CityTreeUtil {

    /**
     * @param cityList
     * @param root
     * @return
     */
    public List<CityTree> buildCityTree(List<City> cityList, int root) {
        List<CityTree> trees = new ArrayList<>();
        CityTree node;
        for (City city : cityList) {
            node = new CityTree();
            BeanUtils.copyProperties(city, node);
            node.setId(city.getId());
            node.setParentId(city.getParentId());
            trees.add(node);
        }
        return TreeUtil.buildByLoop(trees, root);
    }
}
  • 其中用到的 TreeUtil 工具类
@UtilityClass
public class TreeUtil {

    /**
     * 两层循环实现建树
     * @param treeNodes 传入的树节点列表
     * @return
     */
    public <T extends TreeNode> List<T> buildByLoop(List<T> treeNodes, Object root) {
        List<T> trees = new ArrayList<>();
        for (T treeNode : treeNodes) {
            if (root.equals(treeNode.getParentId())) {
                trees.add(treeNode);
            }
            for (T it : treeNodes) {
                if (it.getParentId().equals(treeNode.getId())) {
                    if (treeNode.getChildren() == null) {
                        treeNode.setChildren(new ArrayList<>());
                    }
                    treeNode.add(it);
                }
            }
        }
        return trees;
    }

    /**
     * 使用递归方法建树
     * @param treeNodes
     * @return
     */
    public <T extends TreeNode> List<T> buildByRecursive(List<T> treeNodes, Object root) {
        List<T> trees = new ArrayList<T>();
        for (T treeNode : treeNodes) {
            if (root.equals(treeNode.getParentId())) {
                trees.add(findChildren(treeNode, treeNodes));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    public <T extends TreeNode> T findChildren(T treeNode, List<T> treeNodes) {
        for (T it : treeNodes) {
            if (it.getParentId().equals(treeNode.getId())) {
                if (treeNode.getChildren() == null) {
                    treeNode.setChildren(new ArrayList<>());
                }
                treeNode.add(findChildren(it, treeNodes));
            }
        }
        return treeNode;
    }


    /**
     * 查找TreeNode集合中 所有没有子节点的元素的id
     * @param roleTree
     * @return
     */
    public Set<Object> filterChild(List<TreeNode> roleTree) {
        Set<Object> grantMenu = new TreeSet<>();
        roleTree.forEach(treeNode -> {
            grantMenu.addAll(recursion(treeNode, grantMenu));
        });
        return grantMenu;
    }

    public Set<Object> recursion(TreeNode root, Set<Object> result) {
        List<TreeNode> treeNodes = root.getChildren();
        // TODO 没有子节点的直接加入数组
        if (treeNodes == null || treeNodes.size() <= 0) {
            result.add(root.getId());
            //如果该节点没有子节点,将其加入解集,并结束递归
            return result;
        }

        // TODO 有子节点则遍历后在查找
        for (TreeNode treeNode : treeNodes) {
            recursion(treeNode, result);
        }
        //否则的话,对其每一个子节点执行递归函数
        return result;
    }
}

6、创建相应的 Controller

@RestController
@RequestMapping("/city")
public class CityController {

    @Autowired
    private CityService cityService;

    @GetMapping("/tree")
    public Result buildTree(){
        List<CityTree> cityTrees = cityService.buildTree();
        return Result.success(cityTrees);
    }
}

7、结果

{
    "code": 200,
    "msg": "success",
    "data": [
        {
            "id": 1,
            "parentId": 0,
            "children": [
                {
                    "id": 2,
                    "parentId": 1,
                    "children": [
                        {
                            "id": 3,
                            "parentId": 2,
                            "children": [],
                            "name": "信丰县",
                            "nameEn": "XinFeng"
                        },
                        {
                            "id": 5,
                            "parentId": 2,
                            "children": [],
                            "name": "兴国县",
                            "nameEn": "XinGuo"
                        }
                    ],
                    "name": "赣州市",
                    "nameEn": "GanZhou"
                },
                {
                    "id": 4,
                    "parentId": 1,
                    "children": [],
                    "name": "南昌市",
                    "nameEn": "NanChang"
                }
            ],
            "name": "江西省",
            "nameEn": "JiangXi"
        }
    ]
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总是提示已注册

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值