Java树构建工具

在日常开发中,我们往往会使用到树形结构数据,以满足我们的需求。常常以 parentId​ 来进行关联、储存,但是在我们取到这些数据后,又要经过一定的处理才能形成树结构,往往使用 children​ 来保存子集数据。我们不可能每次需要树形结构就重新写一遍处理方法,其实这些处理方法有共同性,都是循环然后递归取值、赋值,我们将这些相同的逻辑进行抽离,然后在往往需要处理的地方添加回调函数,这样既能完美通用的构建树形结构数据,又能够单独处理。

1. 代码

下面是树构建工具,为了方便使用,我们将方法写成了非静态方法。

package com.yunku.common.utils;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树工具
 *
 * @author Jalon
 * @since 2023/11/2 14:24
 **/
@Data
public class TreeUtils<T> {

    /**
     * ID字段名
     */
    private String idField;

    /**
     * 父级ID字段名
     */
    private String parentIdField;

    /**
     * 子集字段名
     */
    private String childField;

    /**
     * 数据列表
     */
    private List<T> objs;

    public TreeUtils() {

    }

    public TreeUtils(String idField, String parentIdField, String childField) {
        this.idField = idField;
        this.parentIdField = parentIdField;
        this.childField = childField;
    }

    /**
     * 过滤掉没有子集的数据
     */
    public List<T> filter() {
        return this.objs.stream().filter(t -> {
            Object fieldValue = getField(t, this.childField);
            List<Object> objects = this.objToList(fieldValue);
            return Objects.nonNull(objects) && !objects.isEmpty();
        }).collect(Collectors.toList());
    }

    /**
     * 构建树形结构
     *
     * @param objs 数据列表
     */
    public TreeUtils<T> buildTree(List<T> objs) {
        buildTree(objs, null, null);
        return this;
    }

    /**
     * 构建树形结构
     *
     * @param objs 数据列表
     */
    public TreeUtils<T> buildTree(List<T> objs, Function<MapperParam, T> mapper) {
        buildTree(objs, mapper, null);
        return this;
    }

    /**
     * 构建树形结构
     *
     * @param objs 数据列表
     */
    public void buildTreeLeaf(List<T> objs, Function<T, T> leafMapper) {
        buildTree(objs, null, leafMapper);
    }

    /**
     * 构建树形结构
     *
     * @param objs 数据列表
     */
    public void buildTree(List<T> objs, Function<MapperParam, T> mapper, Function<T, T> leafMapper) {
        try {
            for (T obj : objs) {
                toBuild(objs, obj, mapper, leafMapper);
            }
            this.objs = objs;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 递归赋值
     */
    public void toBuild(List<T> objs, T obj, Function<MapperParam, T> mapper, Function<T, T> leafMapper) throws NoSuchFieldException, IllegalAccessException {
        List<T> childrenList = Lists.newArrayList();
        for (T t : objs) {
            Object value = getField(t, this.parentIdField);
            Object value2 = getField(obj, this.idField);

            if (Objects.nonNull(value) && value.equals(value2)) {
                childrenList.add(t);
            }
        }
        if (!childrenList.isEmpty()) {
            setField(obj, this.childField, childrenList);

            for (T children : childrenList) {
                if (Objects.nonNull(mapper)) {
                    toBuild(objs, mapper.apply(new MapperParam(children, obj)), mapper, leafMapper);
                } else {
                    toBuild(objs, children, mapper, leafMapper);
                }
            }
        } else {
            if (Objects.nonNull(leafMapper)) {
                leafMapper.apply(obj);
            }
        }
    }

    /**
     * 获取属性值
     *
     * @param obj 类
     * @return 值
     */
    private Object getField(T obj, String fieldName) {
        try {
            Class<?> clazz = obj.getClass();
            List<Field> fieldList = getFieldByCurrentAndSuper(clazz);
            if (fieldList.size() > 0) {
                for (Field field : fieldList) {
                    if (field.getName().equals(fieldName)) {
                        // 设置可访问私有属性
                        field.setAccessible(true);
                        return field.get(obj);
                    }
                }
            }
        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 赋值属性
     */
    private static <T, S> void setField(T obj, String fieldName, S value) throws IllegalAccessException {
        try {
            Class<?> clazz = obj.getClass();
            List<Field> fieldList = getFieldByCurrentAndSuper(clazz);
            if (fieldList.size() > 0) {
                for (Field field : fieldList) {
                    if (field.getName().equals(fieldName)) {
                        // 设置可访问私有属性
                        field.setAccessible(true);
                        field.set(obj, value);
                    }
                }
            }
        } catch (Exception ignored) {
        }
    }

    /**
     * description: 从当前以及父类中获取全部字段
     *
     * @param clazz 属性所在类
     * @return java.lang.reflect.Field
     */
    public static List<Field> getFieldByCurrentAndSuper(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        getFieldByCurrentAndSuper(clazz, fields);
        return fields;
    }

    /**
     * description: 从当前以及父类中获取全部字段
     *
     * @param clazz 属性所在类
     * @return java.lang.reflect.Field
     */
    private static List<Field> getFieldByCurrentAndSuper(Class<?> clazz, List<Field> fields) {
        Field[] declaredFields = clazz.getDeclaredFields();
        fields.addAll(Arrays.asList(declaredFields));
        if (!clazz.equals(Object.class)) {
            return getFieldByCurrentAndSuper(clazz.getSuperclass(), fields);
        }
        return fields;
    }

    /**
     * 对象转List
     */
    public List<Object> objToList(Object obj) {
        if (obj instanceof ArrayList<?>) {
            return new ArrayList<Object>((List<?>) obj);
        }
        return null;
    }

    @Data
    @AllArgsConstructor
    public class MapperParam {

        /**
         * 当前数据
         */
        private T current;

        /**
         * 父级数据
         */
        private T parent;
    }

}

2. 使用

使用方式也很简单。

设我们有以下数据

List<Book> books = bookMapper.list();

2.1 常规使用

TreeUtils<HcBayonetVo> treeUtils = new TreeUtils<>("bookId", "parentId", "children");
treeUtils.buildTree(books);

// 直接返回,包含所有数据,且包含子集数据
return books;

// 如果想只返回有子集的数据
return books.filter();

2.2 数据项处理

注意:回调不会处理根节点数据项,需要单独循环处理

对于子集,我们直接添加回调即可

treeUtils.buildTree(list, mapperParam -> {
	// mapperParam 拥有两个参数,current 为当前数据项,parent 为当前数据项的父级数据项
    Book parent = mapperParam.getParent();
    Book current = mapperParam.getCurrent();
    current.setLevel(parent.getLevel() + 1);
    return current;
});

叶子节点我们也可以单独处理

treeUtils.buildTreeLeaf(list, book -> {
    book.setLeaf(true);
    return book;
});

下面是完整的处理(给每一项添加级别 level, 叶子节点添加叶子标识 leaf)

for (Book book : list) {
  book.setLevel(1);
}
TreeUtils<Book> treeUtils = new TreeUtils<>("bayonetId", "parentId", "children");
treeUtils.buildTree(books, mapperParam -> {
    Book parent = mapperParam.getParent();
    Book current = mapperParam.getCurrent();
    current.setLevel(parent.getLevel() + 1);
    return current;
}, book -> {
    book.setLeaf(true);
    return book;
});
return treeUtils.filter();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

会功夫的李白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值