§ 1.数据
操作基础节点类,提供主要字段id、名称、父id、内置排序序号、子节点信息创造满足基础业务场景的json数据,数据特点如下
- 一级节点没有子节点
- 一级节点和二级节点没有三级节点
- 一二三级节点
{
"children": [
{
"children": [
{
"name": "test111",
"id": 111,
"parentId": 11
}
],
"name": "test11",
"id": 11,
"parentId": 1
},
{
"name": "test12",
"id": 12,
"parentId": 1
}
],
"name": "test10",
"id": 1,
"parentId": 0
},
{
"children": [
{
"children": [
{
"name": "test211",
"id": 211,
"parentId": 21
},
{
"name": "test212",
"id": 212,
"parentId": 21
}
],
"name": "test21",
"id": 21,
"parentId": 2
}
],
"name": "test20",
"id": 2,
"parentId": 0
},
{
"name": "test30",
"id": 3,
"parentId": 0
}
]
统一一级父节点id为0,如替换,需要替换为区别于其他节点id的唯一值
/** 默认一级节点父id */
public static final Integer DEFAULT_PARENT_ID = 0;
§ 2.数据处理 层级 → 扁平化
/**
* 解析树节点信息 层级 -> 扁平化
* @param nodes 树结构数据
* @return 扁平数据
*/
public List<Node> parseTree(List<Node> nodes){
return Stream.concat(
//第一层流
nodes.stream()
,nodes.stream().flatMap(node->
Stream.concat(
//第二层流
Optional.ofNullable(node.getChildren()).orElseGet(ArrayList::new).stream(),
//第三层流
Optional.ofNullable(node.getChildren()).orElseGet(ArrayList::new).stream()
.flatMap(subNode->Optional.ofNullable(subNode.getChildren()).orElseGet(ArrayList::new).stream())
))).map(this::covertNewNode).collect(Collectors.toList());
}
/**
* 数据转化为新的类,可通过其他工具替换数据转化类
* @param node 原始节点
* @return 新节点
*/
public Node covertNewNode(Node node){
Node newNode = new Node();
newNode.setId(node.getId());
newNode.setName(node.getName());
newNode.setParentId(node.getParentId());
newNode.setOrderNo(node.getOrderNo());
return newNode;
}
扁平化 nodes1 返回结果
§ 3.数据处理 扁平化 → 层级
Version 1.0 支持自定义排序方式 默认升序
/**
* 生成树节点结构 扁平化 -> 层级
* @param nodes 扁平化数据
* @param nodeOrderMap 自定义顺序集合
* @return 排序好的扁平化数据
*/
public List<Node> generateTree(List<Node> nodes, Map<Integer, List<Integer>> nodeOrderMap){
Map<Integer, List<Integer>> orderMap = Optional.ofNullable(nodeOrderMap).orElseGet(HashMap::new);
List<Integer> nodeOrderIds = orderMap.get(DEFAULT_PARENT_ID);
return nodes.stream()
.filter(node-> Objects.equals(node.getParentId(), DEFAULT_PARENT_ID))
.map(node -> covert(nodes,orderMap,node))
.sorted(Comparator.comparing(subNode-> {
if (CollectionUtils.isNotEmpty(nodeOrderIds)) {
return nodeOrderIds.indexOf(subNode.getId());
}else {
return subNode.getOrderNo() == null ? subNode.getId() : subNode.getOrderNo();
}
})).collect(Collectors.toList());
}
/**
* 数据层级递归转化
* @param nodes 扁平化数据
* @param orderMap 自定义顺序集合
* @param node 上层节点
* @return 排序整理好的分支节点
*/
private Node covert(List<Node> nodes,Map<Integer, List<Integer>> orderMap,Node node) {
Stream<Node> nodeStream = nodes.stream().filter(subNode -> Objects.equals(subNode.getParentId(), node.getId()))
.map(subNode -> covert(nodes, orderMap, subNode));
//排序处理
List<Integer> nodeOrderIds = orderMap.get(node.getId());
if (CollectionUtils.isNotEmpty(nodeOrderIds)) {
//1.自定义排序排序方案
nodeStream = nodeStream.sorted(Comparator.comparing(subNode -> nodeOrderIds.indexOf(subNode.getId())));
}else {
//2.默认内置的排序方案 如没有排序指定,兜底使用节点id排序
nodeStream = nodeStream.sorted(Comparator.comparing(orderNode ->
orderNode.getOrderNo() == null ? orderNode.getId() : orderNode.getOrderNo()));
}
//数据搜集赋值
node.setChildren(nodeStream.collect(Collectors.toList()));
return node;
}
当前方法满足了基础生成树层级的方式,如果需要使用指定排序方式,直接传输null或者空集合即可,排序第一优先级是orderMap自定义顺序,第二优先级是orderNo 内置排序顺序,第三优先级是节点id顺序,默认是升序排序,如果希望控制升降顺序呢,我们需要对该方法进行改造
Version 1.1 提取比较器外置,灵活比较
/**
* 生成树节点结构 扁平化 -> 层级
* @param nodes 扁平化数据
* @param comparator 自定义比较器
* @return 排序好的扁平化数据
*/
public List<Node> generateTree(List<Node> nodes,Comparator<Node> comparator){
//默认兜底比较器
if (comparator == null) {
comparator = (n1, n2) -> (n1.getOrderNo() != null && n1.getOrderNo() != null)
? n1.getOrderNo() - n2.getOrderNo()
: n1.getId() - n2.getId();
}
Comparator<Node> finalComparator = comparator;
return nodes.stream()
.filter(node-> Objects.equals(node.getParentId(), DEFAULT_PARENT_ID))
.map(node -> covert(nodes, finalComparator,node))
.sorted(comparator).collect(Collectors.toList());
}
/**
* 数据层级递归转化
* @param nodes 扁平化数据
* @param comparator 自定义比较器
* @param node 上层节点
* @return 排序整理好的分支节点
*/
private Node covert(List<Node> nodes,Comparator<Node> comparator,Node node) {
List<Node> children = nodes.stream().filter(subNode -> Objects.equals(subNode.getParentId(), node.getId()))
.map(subNode -> covert(nodes, comparator, subNode))
.sorted(comparator)
.collect(Collectors.toList());
node.setChildren(children);
return node;
}
业务场景的排序会千万变,所以抽象出比较器,让外部来控制选择器的,更为灵活的控制自定义的升降序,如下是测试 Demo
@Test
public void nodeOrder(){
String json = "[{\"children\":[{\"children\":[{\"children\":[],\"id\":111,\"name\":\"test111\",\"parentId\":11}],\"id\":11,\"name\":\"test11\",\"parentId\":1},{\"children\":[],\"id\":12,\"name\":\"test12\",\"parentId\":1}],\"id\":1,\"name\":\"test10\",\"parentId\":0},{\"children\":[{\"children\":[{\"children\":[],\"id\":211,\"name\":\"test211\",\"parentId\":21,\"orderNo\":1},{\"children\":[],\"id\":212,\"name\":\"test212\",\"parentId\":21,\"orderNo\":0}],\"id\":21,\"name\":\"test21\",\"parentId\":2}],\"id\":2,\"name\":\"test20\",\"parentId\":0},{\"children\":[],\"id\":3,\"name\":\"test30\",\"parentId\":0}]";
List<Node> nodes = JSONArray.parseArray(json, Node.class);
//树结构扁平化处理
List<Node> nodes1 = parseTree(nodes);
Map<Integer, List<Integer>> nodeOrderMap = new HashMap<>();
nodeOrderMap.put(DEFAULT_PARENT_ID, Lists.newArrayList(2, 3, 1));
//比较器
Comparator<Node> comparator = (n1, n2) -> {
List<Integer> nodeOrderIds = nodeOrderMap.get(n1.getParentId());
if (CollectionUtils.isNotEmpty(nodeOrderIds)) {
//1.自定义排序排序方案
return nodeOrderIds.indexOf(n1.getId()) - nodeOrderIds.indexOf(n2.getId());
} else {
//2.默认内置的排序方案 如没有排序指定,兜底使用节点id排序
return (n1.getOrderNo() == null || n2.getOrderNo() == null) ? n1.getId() - n2.getId() : n1.getOrderNo() - n2.getOrderNo();
}
};
//自定义升序
List<Node> nodes3 = generateTree(nodes1, comparator);
System.out.println(JSON.toJSONString(nodes3));
//自定义降序
List<Node> nodes4 = generateTree(nodes1, comparator.reversed());
System.out.println(JSON.toJSONString(nodes4));
}
自定义升序 nodes3 返回结果 自定义降序 nodes4 返回结果
Version 1.2 支持名称模糊匹配
节点名称模糊匹配好处理,处理难点在于关联节点的处理。首先先拿到模糊匹配名称的节点,在扁平流匹配关联节点,组合流数据汇总为集合数据,再层级嵌套数据生成。
/**
* 通过名称匹配数据
*
* @param nodeList 扁平化数据
* @param nameKey 名称key
* @return 匹配节点数据
*/
public static List<Node> matchByName(List<Node> nodeList, String nameKey) {
//生成节点Map
Map<Integer, Node> nodeMap = nodeList.stream().collect(Collectors.toMap(Node::getId, x -> x));
return nodeMap.entrySet().stream().filter(x -> StringUtils.upperCase(x.getValue().getName()).indexOf(nameKey.toUpperCase()) != -1)
.flatMap(x -> {
Map<Integer, Node> tempMap = new HashMap<>();
//填充本节点
tempMap.put(x.getKey(), x.getValue());
Node node = x.getValue();
Integer parentId = node.getParentId();
//寻找父节点信息
do {
Node pNode = nodeMap.get(parentId);
if (pNode == null) {
break;
}
//填充关联节点
tempMap.put(parentId, pNode);
parentId = pNode.getParentId();
} while (true);
//数据组装传递
return tempMap.entrySet().stream();
}).distinct().map(Map.Entry::getValue).collect(Collectors.toList());
}
通过parseTree解析json树结构为扁平化,然后处理数据测试 Demo
@Test
public void matchName(){
//匹配主键
String key = "One";
String json = "[{\"id\":1,\"name\":\"testOne10\",\"parentId\":0,\"children\":[{\"id\":11,\"name\":\"test11\",\"parentId\":1,\"children\":[{\"id\":111,\"name\":\"test111\",\"parentId\":11}]},{\"id\":12,\"name\":\"test12\",\"parentId\":1}]},{\"id\":2,\"name\":\"test20\",\"parentId\":0,\"children\":[{\"id\":21,\"name\":\"test21\",\"parentId\":2,\"children\":[{\"id\":211,\"name\":\"testOne211\",\"parentId\":21,\"orderNo\":1},{\"id\":212,\"name\":\"testOne212\",\"parentId\":21,\"orderNo\":0}]}]},{\"id\":3,\"name\":\"test30\",\"parentId\":0}]";
List<Node> nodes = JSONArray.parseArray(json, Node.class);
//树结构扁平化处理
List<Node> nodes1 = parseTree(nodes);
//树结构组装成型:指定自定义顺序
Map<Integer, List<Integer>> nodeOrderMap = new HashMap<>();
nodeOrderMap.put(DEFAULT_PARENT_ID, Lists.newArrayList(2,3,1));
//比较器
Comparator<Node> comparator = (n1, n2) -> {
List<Integer> nodeOrderIds = nodeOrderMap.get(n1.getParentId());
if (CollectionUtils.isNotEmpty(nodeOrderIds)) {
//1.自定义排序排序方案
return nodeOrderIds.indexOf(n1.getId()) - nodeOrderIds.indexOf(n2.getId());
} else {
//2.默认内置的排序方案 如没有排序指定,兜底使用节点id排序
return (n1.getOrderNo() == null || n2.getOrderNo() == null) ? n1.getId() - n2.getId() : n1.getOrderNo() - n2.getOrderNo();
}
};
//自定义升序
List<Node> nodes5 = generateTree(matchByName(nodes1,key),comparator);
System.out.println(JSON.toJSONString(nodes5));
}
自定义排序名称 nodes5 返回结果