列表与树的处理

这篇博客介绍了一个泛型树工具类的实现,用于将具有parent_id字段的数据库列表转换成树结构。通过定义TreeAble接口和TreeNode类,实现了对象到树节点的转换。工具类包括创建树、查找树中特定节点的所有子节点等功能,适用于具有父子关系的数据转换。同时,文章提供了一个TreeDemo类作为示例,展示了如何使用该工具类进行转换和查找操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

列表与树的处理

最近做一个功能需要将数据库的数据转换成树结构,可以转换成树的数据往往数据库里的表字段含有parent_id这种代表关系,突发奇想,做一个泛型的树工具类,以后遇到这种结构都可以直接转换成树使用。

对象必须具有转换成树的能力

适用于具有父子关系的属性的列表与树的转换,这里封装成了接口TreeAble,提供了找到父节点和自身的索引,其中泛型K是索引的类型。

public interface TreeAble<K> {

    K getKey();

    K getParentKey();

}

树节点对象

每个树的节点应该是有索引,子节点和树节点的值等属性,完善一点可以包含表示父节点的属性,这里不再冗余,以下结构已经可以表示树的结构了。这里使用了lombok的注解省去了构造器和get,set方法,其中泛型V即是树节点的值。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TreeNode<K, V> {
    private K key;

    private V value;

    private List<TreeNode<K, V>> children;

}

将列表转化成树

准备工作做好后,就可以写工具类了,首先是将数据库里查找出来的列表对象转化成树结构

public static <K, V extends TreeAble<K>> List<TreeNode<K, V>> createTreeByList(List<V> list) {
    //用于存放树节点,方便找到
    HashMap<K, TreeNode<K, V>> map = new HashMap<>();
    //用于存放未找到父节点的对象,此类对象进入下次循环,直到所有对象找到父节点
    Set<V> noParent = new HashSet<>();
    //虚拟一个最顶层的节点
    TreeNode<K, V> top = new TreeNode<>();
    //容错处理,循环20次未找到父节点,证明list可能存在环结构,父节点不存在的对象,否则可能出现死循环
    int maxNum = 20;
    while (list.size() > 0) {
        for (V v : list) {
            //新建树节点,并存放到map中,方便子节点
            TreeNode<K, V> treeNode = new TreeNode<>(v.getKey(), v, null);
            map.put(v.getKey(), treeNode);
            //父节点为null或者父节点是自己,说明是顶层节点,他的节点就是虚拟顶层节点
            TreeNode<K, V> parent = v.getParentKey() == null || v.getParentKey().equals(v.getKey()) ? top : map.get(v.getParentKey());
            //父节点如果没有找到,存放到noParent,下次便利是继续
            if (parent == null) {
                noParent.add(v);
                continue;
            }
            //父节点如果找到了,添加到父节点
            if (parent.getChildren() == null) {
                parent.setChildren(new ArrayList<>());
            }
            parent.getChildren().add(treeNode);
        }
        //重新设置list为没有找到父节点的对象,进入下次循环
        list = new ArrayList<>(noParent);
        noParent.clear();
        //死循环容错处理
        maxNum--;
        if (maxNum == 0) {
            throw new IllegalArgumentException("参数异常,不能组成树结构,请检查是否为(环,父节点不存在,数据过于庞大)");
        }
    }
    //最后返回虚拟顶层节点的子节点既是列表转换后的树结构
    return top.getChildren();
}

可以扩展成list转化成单个树

public static <K, V extends TreeAble<K>> TreeNode<K, V> createSingleTreeByList(List<V> list) {
    List<TreeNode<K, V>> treeNodes = createTreeByList(list);
    if (treeNodes == null || treeNodes.size() != 1) {
        throw new IllegalArgumentException("参数异常,不是简单树结构");
    }
    return treeNodes.get(0);
}

查找一个树下面所有的节点

仅仅转换成树可能还不够,在实际应用中还有很多是需要进一步处理的,比如需要查看一个树下面所有的节点。

public static <K, V extends TreeAble<K>> List<V> getAllChildrenByKey(List<TreeNode<K, V>> tree, K key) {
    //采用层级查找的方法,topTree缓存下一次需要查找的层级的所有树节点,知道层级到最后一层
    List<TreeNode<K, V>> topTree = new ArrayList<>();
    while (tree.size() > 0) {
        for (TreeNode<K, V> node : tree) {
            //当遍历的树节点的key等于目标key时,找到这个节点下的的所有对象,并返回
            if (key.equals(node.getKey())) {
                List<V> allNode = getAllNode(node.getChildren());
                allNode.add(node.getValue());
                return allNode;
            } else {
                //如果没找到目标key对应的节点,进入下一个层级搜索
                if (node.getChildren() != null) {
                    topTree.addAll(node.getChildren());
                }
            }
        }
        tree = new ArrayList<>(topTree);
        topTree.clear();
    }
    return new ArrayList<>();
}
//找到这些节点下的的所有对象
public static <K, V extends TreeAble<K>> List<V> getAllNode(List<TreeNode<K, V>> tree) {
    List<V> result = new ArrayList<>();
    if (tree != null) {
        for (TreeNode<K, V> treeNode : tree) {
            //如果有子节点,递归查询这些节点下的的所有对象,最后加上自己本身
            if (treeNode.getChildren() != null) {
                result.addAll(getAllNode(treeNode.getChildren()));
            }
            result.add(treeNode.getValue());
        }
    }
    return result;
}

可以扩展成可树化的列表查找下方所有的节点

public static <K, V extends TreeAble<K>> List<V> getAllChildrenByKeyFromList(List<V> tree, K key) {
    List<TreeNode<K, V>> treeByList = createTreeByList(tree);
    return getAllChildrenByKey(treeByList, key);
}

测试类

可树化的类:

@Data
@AllArgsConstructor
public class TreeDemo implements TreeAble<String> {
    private String id;
    private String parentId;

    @Override
    public String getKey() {
        return id;
    }

    @Override
    public String getParentKey() {
        return parentId;
    }
}

Main函数:

public static void main(String[] args) {
    List<TreeDemo> treeDemos = Arrays.asList(
        new TreeDemo("1", "1"),
        new TreeDemo("2", "1"),
        new TreeDemo("3", "1"),
        new TreeDemo("4", "1"),
        new TreeDemo("5", "3"),
        new TreeDemo("6", "2"),
        new TreeDemo("7", "5"),
        new TreeDemo("8", "7"),
        new TreeDemo("9", "9"),
        new TreeDemo("10", "9"),
        new TreeDemo("11", "9"),
        new TreeDemo("12", "2"),
        new TreeDemo("13", "4"),
        new TreeDemo("14", "6"),
        new TreeDemo("15", "6"),
        new TreeDemo("16", "1")
    );
    List<TreeNode<String, TreeDemo>> treeNodes = TreeUtil.createTreeByList(treeDemos);
    //TreeNode<String, TreeTest> treeNodes = TreeUtil.creteSingleTreeByList(treeTests);
    System.out.println(treeNodes);
    //List<TreeDemo> allChildrenByKey = TreeUtil.getAllChildrenByKey(treeNodes, "9");
    List<TreeDemo> allChildrenByKey = TreeUtil.getAllChildrenByKeyFromList(treeDemos, "1");
    System.out.println(allChildrenByKey);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值