玄星幻月001-树结构复制

玄星幻月001-树结构复制

前言:在实际开发中,可能会遇到一些通用的代码块,临时想比较费时耗力,如果做好记录就能很方便的拿来即用,省时省力。该系列将会记录本人项目或其他地方遇到比较好的例子来展开记录。玄星幻月:高中就开始喜欢这个词了,就以此作为系列名称!先定个小目标,数量突破三位数。(^_^)

背景:

树结构是在项目中比较常见的一种结构,向单项列表一样,每个节点属性都会存放着下一节点的id。有时候需要将一棵树复制并另存为另外的树,但是由于表主键的唯一性,如何保证复制后在每个节点主键变化的提前下又保证树的结构呢?

现在通过主键映射的方式来复制树结构,这样的好处是只需要按照原来的树结构映射替换它的主键,就可以保证树结构不变的又实现树的复制功能。

案例
节点信息
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class TreeNode {
    /**
     * 节点名称
     */
    private String nodeName;

    /**
     * 节点Id
     */
    private String nodeId;

    /**
     * 节点Id
     */
    private String nodePid;

    /**
     * 为了方便测试,初始化一棵树
     * 
     * @return List<TreeNode>
     */
    public static List<TreeNode> createTreeA() {
        TreeNode ziyang = new TreeNode("ziyang", "a1", "-1");
        TreeNode ziyue = new TreeNode("ziyue", "a2", "a1");
        TreeNode zixing = new TreeNode("zixing", "a3", "a2");
        TreeNode zichen = new TreeNode("zichen", "a4", "a3");
        return new ArrayList<>(Arrays.asList(ziyang, ziyue, zixing, zichen));
    }
原树图示

原树图示
此树结构有4层,跟节点是"ziyang",其父节点id为 -1 ,(父节点是 -1 表示根节点);下一节点是 “ziyue”,以此类推。

复制树示例【核心】

step1:生成Id映射Map

/**
 * 生成Id映射Map
 *
 * @param treeA 原树节点集合
 * @return 原树和新树Id映射Map
 */
private static Map<String, String> getOldNewMap(List<TreeNode> treeA) {
    // 所有节点id集合
    List<String> nodeIdList = treeA.stream().map(TreeNode::getNodeId).collect(Collectors.toList());
    // 获取原树的跟节点id
    String rootId = treeA.stream().filter(item -> item.getNodePid().equals("-1"))
            .collect(Collectors.toList())
            .get(0).getNodeId();
    // 新树跟节点newId期前指定
    // 根据实际情况调整
    String newId = "b1";
    Map<String, String> idMap = new HashMap<>(treeA.size());
    for (String nodeId : nodeIdList) {
        // 先找到根节点,(根节点的nodePid都是 -1)
        if (nodeId.equals(rootId)) {
            idMap.put(rootId, newId);
        } else {
            // 用 UUID 生成 新树和旧树的id映射关系
            // 根据实际id策略生成,为了方便此处使用UUID
            idMap.put(nodeId, UUID.randomUUID().toString());
        }
    }
    return idMap;
}

step2:生成复制后的树结构

/**
 * 生成复制后的树结构
 * 
 * @param treeA 原树节点集合
 * @param idMap 原树和新树Id映射Map,由 step1 生成
 * @return 改变id的树节点集合
 */
private static List<TreeNode> getTreeB(List<TreeNode> treeA, Map<String, String> idMap) {
    List<TreeNode> treeB = new ArrayList<>(treeA.size());
    for (TreeNode treeNode : treeA) {
        String id = idMap.get(treeNode.getNodeId());
        // 可不判空,适用于根节点标记不是 nodePid
        if (null != id) {
            treeNode.setNodeId(id);
        }
        String pid = idMap.get(treeNode.getNodePid());
        // 跟节点标记在父ID,即 nodePid = -1
        // 跟节点的标志 nodePid = -1 不在映射中时不用重新复制,在 idMap 中也取不到,不用处理
        if (null != pid) {
            treeNode.setNodePid(pid);
        }
        treeB.add(treeNode);
    }
    return treeB;
}
新树结构

新树结构

测试用例
@Test
public void testCopyTest() {
    // 获取原树节点集合
    List<TreeNode> treeA = TreeNode.createTreeA();
    System.out.println("treeA : " + treeA);
    // 生成原树和新树Id映射Map
    Map<String, String> idMap = getOldNewMap(treeA);
    // 获取复制后的树节点
    List<TreeNode> treeB = getTreeB(treeA, idMap);
    System.out.println("treeB : " + treeB);
}

后记:玄星幻月的第一篇完全是自己想的,遇到这个场景的时候也在网上搜过,但是没有找到类型的例子,也许用到的场景不是很多,但是感觉比较独特,就记录下来,若有更好的树复制方式,欢迎讨论,当然有其他比较有趣的例子,也可以共同探讨。(^_^)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值