0
点赞
收藏
分享

微信扫一扫

java 8 数据树层级关系嵌套自定义排序生成和扁平化

沐之轻语 2022-04-14 阅读 80
java

§ 1.数据

操作基础节点类,提供主要字段id、名称、父id、内置排序序号、子节点信息创造满足基础业务场景的json数据,数据特点如下

  1. 一级节点没有子节点
  2. 一级节点和二级节点没有三级节点
  3. 一二三级节点

  {
    "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 返回结果 

Version X 后续 ... ...

举报

相关推荐

0 条评论