函数式树组成

  • 定义实体类 例:

@Data
@ExcelIgnoreUnannotated
public class CourseVo {

    private static final long serialVersionUID = 1L;

    /**
     * 课程id
     */
    private String courseId;

    /**
     * 课程名称
     */
    private String name;

    /**
     * 类型
     */  
     private String type;

    /**
     *
     */
    private String parentId;

    /**
     * 排序
     */
    private String sort;

    /**
     * 备注
     */
    private String remark;


    /**
     * 子项目
     */
    private List<CourseVo> children;

}
  • 定义 工具类

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.lang.tree.parser.NodeParser;
import com.corecode.common.utils.reflect.ReflectUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.Assert;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 扩展 hutool TreeUtil 封装系统树构建
 *
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeBuildUtils extends TreeUtil {

    /**
     * 根据前端定制差异化字段
     */
    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");

    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
    }


    /**
     * 构造数据树 O(N)
     *
     * @param list          原集合
     * @param key           父级被子集关联的字段,比如id
     * @param parentKey     子集关联父级的字段,比如parentId
     * @param sortKey       同级菜单的排序字段,比如sort
     * @param consumer      添加子集的函数,如dto.setChild(list)
     * @param <T>
     * @return
     */
    public static <T> List<T> buildTree(List<T> list, Function<T, ?> key, Function<T, ?> parentKey,
                                        Function<T, ? extends Comparable> sortKey, Consumers<T, T> consumer) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Assert.notNull(key, "父级被子级关联的字段不能为空");
        Assert.notNull(parentKey, "子级关联父级的字段不能为空");
        Assert.notNull(consumer, "消费函数不能为空");
        //线程安全类集合
        List<T> tree = Collections.synchronizedList(new ArrayList<>());
        //按上级id分组
        Map<?, List<T>> collect = Collections.synchronizedMap(list.parallelStream()
            .filter(item -> Objects.nonNull(parentKey.apply(item)))
            .collect(Collectors.groupingBy(parentKey)));
        list.parallelStream()
            .filter(m -> {
                final boolean b = Objects.nonNull(parentKey.apply(m)) && StringUtils.isNotEmpty((String) parentKey.apply(m));
                if (!b) {
                    //收集顶级节点
                    tree.add(m);
                }
                return b;
            })
            .forEach(
                //通过对象地址引用实现父子级关联,即使父级先添加了子级,子级在添加孙子级,父子孙三级也全部都会关联
                node -> {
                    sortFunction(sortKey, consumer, node, collect.get(key.apply(node)));
                }
            );
        //排序
        Stream<T> peek = tree.parallelStream().peek(node ->
            sortFunction(sortKey, consumer, node, collect.get(key.apply(node))
            ));
        return peek.collect(Collectors.toList());
    }

    /**
     * 排序函数
     *
     * @param sortKey 排序字段
     * @param consumer    消费函数
     * @param node        节点
     * @param children    子节点
     * @param <T>         泛型
     */
    private static <T> void sortFunction(Function<T, ? extends Comparable> sortKey, Consumers<T, T> consumer, T node, List<T> children) {
        if (Objects.isNull(children)) {
            return;
        }
        if (Objects.nonNull(sortKey)) {
            try {
                //排序
                Comparator comparing = Comparator.comparing(sortKey);
                List<T> sort = CollUtil.sort(children, comparing);
                consumer.accept(node, sort);
            } catch (NullPointerException e) {
                //如存在排序字段值为空情况会抛出空指针
                consumer.accept(node, children);
            }
        } else {
            consumer.accept(node, children);
        }
    }

    @FunctionalInterface
    public interface Consumers<T, N> {

        /**
         * 消费函数(设置children函数)
         *
         * @param m
         * @param n
         */
        void accept(T m, List<N> n);
    }

}

  • 使用

 List<CourseVo> vos = this.queryList(bo);
 List<CourseVo> tree = TreeBuildUtils.buildTree(vos, CourseVo::getCourseId, CourseVo::getParentId, CourseVo::getSort, CourseVo::setChildren);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值