背景
最近公司有个需求,要自研接口平台,将公司接口对接到接口平台,统一进行管理,其中,新增接口的时候新增参数,参数是一个树形结构,需要先解析出参数,然后将参数的树形结构按层级生成id,和pid, 再转换成数组,获取接口详情的时候需要将这一过程返过来,将参数数组转换成树
树结构
(id, pid)
(1,0)
- (2, 1)
- (3, 1)
- (4, 3)
(5,0)
- (7,5)
(8,0)
- (9,8)
(10,0)
代码
/**
* @author: lpy
* @Date: 2023/01/07
*/
@Data
@Accessors(chain = true)
public class TreeNode {
/**
* id
*/
private Long id;
/**
* 父id
*/
private Long pid;
/**
* 每个节点路径
*/
private String path;
private List<TreeNode> params;
}
完整代码
public class TreesUtil {
private static ArrayList<TreeNode> paramArr;
private static Stack<String> stack;
static {
stack = new Stack<>();
}
/**
* 将数组转换成树
*
* @param treeList 数组
* @param parentId parentId
* @return list
*/
public static List<TreeNode> toTree(List<TreeNode> treeList, Long parentId) {
List<TreeNode> list = treeList.stream()
.filter(parent -> parent.getPid().equals(parentId))
.map(child -> {
child.setParams(toTree(treeList, child.getId()));
return child;
}).collect(Collectors.toList());
return list;
}
/**
* 为树状结构生成id 和 path(父节点到子节点路径)
*
* @param list 树
* @param pid pid
* @return List
*/
public List<TreeNode> generateTreePid(List<TreeNode> list, Long pid) {
for (TreeNode param : list) {
Long id = IdUtil.getSnowflakeNextId();
param.setId(id);
String pidPath = pid == 0L ? "/" : stack.isEmpty() ? "" : stack.peek() + "/";
if (CollectionUtils.isNotEmpty(param.getParams())) {
// 下面有节点要回溯
stack.push(pidPath + id);
generateTreePid(param.getParams(), id);
} else {
String temp = (stack.isEmpty() ? "" : stack.peek()) + "/" + id;
stack.push(temp);
}
param.setPath(stack.pop());
param.setPid(pid);
}
return list;
}
/**
* 将树结构转换成数组
*
* @param list 树
* @return List<>
*/
public List<TreeNode> treeToList(List<TreeNode> list) {
paramArr = new ArrayList<>();
this.addToArr(list);
return paramArr;
}
/**
* 树结构转换成数组
*
* @param list list
* @return List<TreeNode>
*/
private List<TreeNode> addToArr(List<TreeNode> list) {
for (TreeNode apiParam : list) {
if (CollectionUtils.isNotEmpty(apiParam.getParams())) {
addToArr(apiParam.getParams());
}
apiParam.setParams(new ArrayList<>());
paramArr.add(apiParam);
}
return list;
}
}
其中为树生成path想了挺久的,这个不像是二叉树,有左子节点,右子节点,有一个根节点,这个有好多根节点和子节点,
最终采用的方法是递归加回溯,其中回溯我才用了java的栈,弹栈来实现
不采用回溯的话,一开始的节点path是没有问题的,但是下面的节点path会带有起初节点的子节点和父节点路径,
测试代码
public static void main(String[] args) {
ArrayList<TreeNode> list = new ArrayList<>();
list.add(new TreeNode().setId(1L).setPid(0L));
list.add(new TreeNode().setId(2L).setPid(1L));
list.add(new TreeNode().setId(3L).setPid(1L));
list.add(new TreeNode().setId(4L).setPid(3L));
list.add(new TreeNode().setId(5L).setPid(0L));
list.add(new TreeNode().setId(7L).setPid(5L));
list.add(new TreeNode().setId(8L).setPid(0L));
list.add(new TreeNode().setId(9L).setPid(8L));
list.add(new TreeNode().setId(10L).setPid(0L));
ArrayList<TreeNode> listDto = new ArrayList<>();
JSONArray objects = JSON.parseArray(
"[{\"id\":1,\"params\":[{\"id\":2,\"params\":[],\"pid\":1},{\"id\":3,\"params\":[{\"id\":4,\"params\":[],\"pid\":3}],\"pid\":1}],\"pid\":0},{\"id\":5,\"params\":[{\"id\":7,\"params\":[],\"pid\":5}],\"pid\":0},{\"id\":8,\"params\":[{\"id\":9,\"params\":[],\"pid\":8}],\"pid\":0}]\n"
);
for (Object object : objects) {
TreeNode map = BeanConvertorUtils.map(object, TreeNode.class);
listDto.add(map);
}
List<TreeNode> nodeVOList = toTree(list, 0L);
System.out.println(JSON.toJSONString(nodeVOList));
TreesUtil tree = new TreesUtil();
List<TreeNode> TreeNodeS = tree.generateTreePid(listDto, 0L);
System.out.println(JSON.toJSONString(TreeNodeS));
List<TreeNode> TreeNodeS1 = tree.treeToList(TreeNodeS);
System.out.println(JSON.toJSONString(TreeNodeS1));
/**
* [{"apiParamId":1,"fieldType":1,"params":[{"apiParamId":2,"fieldType":2,"params":[],"parentId":1,"remark":"21"},{"apiParamId":3,"fieldType":3,"params":[{"apiParamId":4,"fieldType":4,"params":[],"parentId":3,"remark":"43"}],"parentId":1,"remark":"31"}],"parentId":0,"remark":"1, 0"},{"apiParamId":5,"fieldType":5,"params":[{"apiParamId":7,"fieldType":6,"params":[],"parentId":5,"remark":"75"}],"parentId":0,"remark":"50"},{"apiParamId":8,"fieldType":8,"params":[],"parentId":0,"remark":"80"}]
*
*
* [{"apiParamId":1607261486112108546,"fieldType":1,"params":[{"apiParamId":1607261486112108547,"fieldType":2,"params":[],"parentId":1607261486112108546,"remark":"21"},{"apiParamId":1607261486112108548,"fieldType":3,"params":[{"apiParamId":1607261486112108549,"fieldType":4,"params":[],"parentId":1607261486112108548,"remark":"43"}],"parentId":1607261486112108546,"remark":"31"}],"parentId":0,"remark":"1, 0"},{"apiParamId":1607261486112108550,"fieldType":5,"params":[{"apiParamId":1607261486112108551,"fieldType":6,"params":[],"parentId":1607261486112108550,"remark":"75"}],"parentId":0,"remark":"50"},{"apiParamId":1607261486112108552,"fieldType":8,"params":[],"parentId":0,"remark":"80"}]
*/
}