Java树结构工具类

这是一个Java工具类,用于快速将列表数据转换为树形结构,并支持父子排序。主要方法包括`listToTree`、`treeListToSortList`和`treeOut`,使用了函数式编程接口如`Function`和`BiConsumer`,实现了从Map到树形列表的转换,并能将树形结构展开为排序列表。此外,还涉及到了反射和序列化Lambda表达式的操作。
摘要由CSDN通过智能技术生成
package com.ciih.chatedu.common.util;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.SneakyThrows;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树形工具类-函数版
 *
 * @author sunziwen
 */

public class TreeUtil {


    /**
     * Map版本(速度比递归要快很多)
     * listToTree
     *
     * @param target      需转换的数据
     * @param getId       主键
     * @param getParentId 父id (父id必须和主键相同类型)
     * @param getChildren 子集
     * @param setChildren 子集
     * @return tree
     */
    public static <T, R> List<T> listToTree(List<T> target, Function<T, R> getId, Function<T, R> getParentId,
                                            Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {

        Map<R, T> oldMap = target.stream().collect(Collectors.toMap(getId, T -> T));
        List<T> result = new ArrayList<>();
        target.forEach(tree -> {
            T parent = oldMap.get(getParentId.apply(tree));
            if (parent == null) {
                result.add(tree);
            } else {
                List<T> ch = getChildren.apply(parent);
                if (ch == null) {
                    ch = new ArrayList<>();
                }
                ch.add(tree);
                setChildren.accept(parent, ch);
            }
        });
        return result;
    }

    /**
     * 树列表转父子排序列表且按父在前,子在后,进行排序。
     *
     * @param target 父子树列表(带有id、parentId和children的模型列表)
     * @param getId
     * @param getParentId
     * @param getChildren
     * @param setChildren
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<T> treeListToSortList(List<T> target, Function<T, R> getId, Function<T, R> getParentId,
                                                  Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {
        // 先整成树形结构
        List<T> list = listToTree(target, getId, getParentId, getChildren, setChildren);
        List<T> result = new ArrayList<>();
        tree2List(result, list.get(0), getChildren, setChildren);
        return result;
    }

    /**
     * 将树转成列表。
     *
     * @param result      结果容器
     * @param t           树顶部元素
     * @param getChildren
     * @param <T>
     */
    private static <T> void tree2List(List<T> result, T t, Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {
        //根据条件判断是否需要添加至列表
        result.add(t);
        List<T> children = getChildren.apply(t);
        // 将children置成空
        setChildren.accept(t, null);
        //没有子级
        if (children == null || children.size() == 0) {
            return;
        }
        //存在子级,递归调用
        for (T child : children) {
            tree2List(result, child, getChildren, setChildren);
        }
    }

    /**
     * @param list          树结构的基础数据集
     * @param getIdFn       获取主键的函数
     * @param getParentIdFn 获取父节点的函数
     * @param getChildrenFn 获取子集的函数
     * @param <T>           t
     * @param <R>           r
     * @return t
     */
    @SneakyThrows
    public static <T, R> List<T> treeOut(List<T> list, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        /*所有元素的Id*/
        List<Object> ids = list.stream().map(getIdFn).collect(Collectors.toList());
        /*查出所有顶级节点*/
        List<T> topLevel = list.stream().filter(x -> {
            R apply = getParentIdFn.apply(x);
            return !ids.contains(apply);
        }).collect(Collectors.toList());
        return TreeUtil.recursion(topLevel, list, getIdFn, getParentIdFn, getChildrenFn);
    }
/**
     * 指定顶级元素
     * @param list
     * @param top
     * @param getIdFn
     * @param getParentIdFn
     * @param getChildrenFn
     * @param <T>
     * @param <R>
     * @return
     */
    @SneakyThrows
    public static <T, R> List<T> treeOutWithTop(List<T> list, T top, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        ArrayList<T> ts = new ArrayList<>();
        ts.add(top);
        return TreeUtil.recursion(ts, list, getIdFn, getParentIdFn, getChildrenFn);
    }

    @SneakyThrows
    private static <T, R> List<T> recursion(List<T> superLevel, List<T> list, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        //获取setChildren的Method
        Method writeReplaceMethod = getChildrenFn.getClass().getDeclaredMethod("writeReplace");
        boolean accessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(getChildrenFn);
        writeReplaceMethod.setAccessible(accessible);
        String setMethodName = serializedLambda.getImplMethodName().replaceFirst("g", "s");
        Method setMethod = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredMethod(setMethodName, List.class);

        for (T t : superLevel) {
            List<T> children = list.stream().filter(x -> {
                R apply = getParentIdFn.apply(x);
                R apply1 = getIdFn.apply(t);
                return apply.equals(apply1);
            }).collect(Collectors.toList());
            if (children.size() <= 0) {
                continue;
            }

            List<T> recursion = recursion(children, list, getIdFn, getParentIdFn, getChildrenFn);
            setMethod.invoke(t, recursion);
        }
        return superLevel;
    }


}

以下是一个 Java 工具类,可以将形结构数据输出为字符串: ```java import java.util.List; public class TreePrinter { private static final String LINE_PREFIX = " "; private static final String BRANCH_PREFIX = "│ "; private static final String LAST_BRANCH_PREFIX = "└── "; public static <T extends TreeNode> String printTree(T root) { StringBuilder sb = new StringBuilder(); printNode(sb, "", root, true); return sb.toString(); } private static <T extends TreeNode> void printNode(StringBuilder sb, String prefix, T node, boolean isLast) { sb.append(prefix); if (isLast) { sb.append(LAST_BRANCH_PREFIX); prefix += LINE_PREFIX; } else { sb.append(BRANCH_PREFIX); prefix += BRANCH_PREFIX; } sb.append(node.getName()); sb.append("\n"); List<T> children = node.getChildren(); if (children != null) { int size = children.size(); for (int i = 0; i < size; i++) { boolean last = i == size - 1; printNode(sb, prefix, children.get(i), last); } } } public interface TreeNode { String getName(); List<? extends TreeNode> getChildren(); } } ``` 使用方法: 1. 实现 `TreeNode` 接口,并实现其中的两个方法 `getName()` 和 `getChildren()`,分别返回节点名称和子节点列表。 2. 调用 `TreePrinter.printTree(root)` 方法,其中 `root` 为的根节点。 以下是一个简单的例子: ```java import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { TreeNode root = new Node("root"); Node node1 = new Node("node1"); Node node2 = new Node("node2"); Node node3 = new Node("node3"); Node node4 = new Node("node4"); Node node5 = new Node("node5"); Node node6 = new Node("node6"); root.addChild(node1); root.addChild(node2); root.addChild(node3); node2.addChild(node4); node2.addChild(node5); node3.addChild(node6); String tree = TreePrinter.printTree(root); System.out.println(tree); } static class Node implements TreePrinter.TreeNode { private String name; private List<Node> children; public Node(String name) { this.name = name; this.children = new ArrayList<>(); } public void addChild(Node child) { children.add(child); } @Override public String getName() { return name; } @Override public List<Node> getChildren() { return children; } } } ``` 输出结果为: ``` └── root ├── node1 ├── node2 │ ├── node4 │ └── node5 └── node3 └── node6 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值