TreeListUtil:一键解决树状数据难题

本文介绍了如何使用TreeListUtil工具,通过三个注解快速将扁平数据转换为树状结构,尤其适合处理组织结构、文件系统和分类系统等场景,提升代码可读性和效率。
摘要由CSDN通过智能技术生成

引言

在软件开发的世界中,树状结构是一个常见的数据形式,用于表示各种层次化的信息,如组织结构、文件夹结构和分类系统。但是,要将扁平的数据列表转换为树状结构可能会让人头疼。给大家分享一个工具类,仅需要三个注解,即可生成标准的树状结构数据。

树状结构是什么

树状结构列表是一种常见的数据结构,它模拟了树的形状,由节点组成,具有以下特点:

  • 根节点(Root):树的顶部节点,是树的起始点,没有父节点。
  • 节点(Node):树中的元素,包含数据以及指向子节点的引用。
  • 父节点(Parent Node):每个节点都有一个父节点,除了根节点。
  • 子节点(Child Node):节点下面的直接子元素,可以有多个子节点。

树状结构在编程中的应用场景

  • 文件系统:计算机的文件和文件夹组织通常以树状结构表示,其中根节点是根目录,子节点是文件和文件夹。
  • 组织架构:公司、学校等组织的层次结构可以用树来表示,每个节点代表一个部门或单位。
  • 图形界面控件:GUI应用程序中,用户界面元素(如按钮、文本框)通常以树的形式层次化排列。
  • 分类和标签:在博客、商品等应用中,可以使用树状结构对内容进行分类和标签化


尽管树状结构在编程中有广泛的应用,但将扁平化的数据转换为树状结构可能是一项复杂和繁琐的任务,涉及到递归、节点关系的建立等操作。因此,有时需要一个专门的工具类来简化这个过程

工具类介绍

工具名称:TreeListUtil

用途: TreeListUtil工具类用于处理树状结构的数据,这种数据通常包含父子关系,如组织结构、分类体系、评论回复等。


主要功能

  • 构建树状结构: TreeListUtil工具类可以根据指定的数据列表,将数据构建成树状结构。通常,这个数据列表包含了各个节点的信息,例如ID、父节点ID和子节点列表。
  • 优雅的API: 该工具类提供了简洁的API,使得构建树状结构变得非常容易。你只需提供数据列表,工具类会自动帮你构建出树状结构。
  • 注解支持: 工具类支持使用注解来标识数据对象中的关键字段,如ID、父节点ID和子节点列表字段。这使得工具类可以适用于不同的数据对象。
  • 异常处理: 工具类具备良好的异常处理机制,以确保在使用过程中出现问题时能够提供有用的错误信息,帮助你诊断和解决问题。
  • 类型安全性: 工具类在处理数据对象时,会进行类型安全性检查,以确保字段的类型与预期一致。

工具代码

TreeCruxEnum.java

public enum TreeCruxEnum {
    ID,
    PID,
    CHILD_LIST
}

TreeCruxField.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TreeCruxField {

    TreeCruxEnum value();

}

TreeListUtil.java

import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class TreeListUtil<T> {

    private final Field idField;          // ID字段
    private final Field parentIdField;    // 父节点ID字段
    private final Field childrenField;    // 子节点列表字段

    /**
     * 构造函数,初始化TreeListUtil对象。
     *
     * @param classType 要操作的数据类的Class对象。
     */
    public TreeListUtil(Class<T> classType) {
        // 获取并设置ID字段、父节点ID字段和子节点列表字段
        this.idField = getFieldWithTreeCruxAnnotation(classType, TreeCruxEnum.ID);
        this.parentIdField = getFieldWithTreeCruxAnnotation(classType, TreeCruxEnum.PID);
        this.childrenField = getFieldWithTreeCruxAnnotation(classType, TreeCruxEnum.CHILD_LIST);
    }

    /**
     * 根据注解值获取带有TreeCruxField注解的字段。
     *
     * @param clazz           数据类的Class对象。
     * @param annotationValue 注解值(ID、父节点ID或子节点列表)。
     * @return 带有指定注解的字段。
     */
    private Field getFieldWithTreeCruxAnnotation(Class<T> clazz, TreeCruxEnum annotationValue) {
        List<Field> annotatedFields = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            TreeCruxField annotation = field.getAnnotation(TreeCruxField.class);
            if (annotation != null && annotation.value() == annotationValue) {
                annotatedFields.add(field);
            }
        }
        Assert.isTrue(annotatedFields.size() == 1,
                String.format("class %s TreeCruxField value is more than one attribute for %s ", clazz.getName(), annotationValue));
        Field field = annotatedFields.get(0);
        field.setAccessible(true);  // 设置字段访问权限为可访问
        return field;
    }

    /**
     * 构建树状结构。
     *
     * @param dataList 包含数据的列表。
     * @return 树状结构的根节点列表。
     */
    public List<T> buildTree(List<T> dataList) {
        List<T> treeList = new ArrayList<>();
        dataList.stream()
                .filter(obj -> {
                    try {
                        // 过滤出根节点
                        return 0 == (Long) parentIdField.get(obj);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("The parent ID field is not of type Long", e);
                    }
                })
                .peek(treeList::add)
                .forEach(root -> addChildren(root, dataList));

        return treeList;
    }

    /**
     * 递归添加子节点。
     *
     * @param parent     父节点。
     * @param dataList   包含数据的列表。
     */
    private void addChildren(T parent, List<T> dataList) {
        dataList.stream()
                .filter(data -> {
                    try {
                        // 查找与父节点ID匹配的子节点
                        return parentIdField.get(data).equals(idField.get(parent));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("The ID and parent ID field types are different", e);
                    }
                })
                .peek(child -> {
                    try {
                        // 添加子节点到父节点的子节点列表
                        List<T> childList = (List<T>) childrenField.get(parent);
                        if (childList == null) {
                            childList = new ArrayList<>();
                            childrenField.set(parent, childList);
                        }
                        childList.add(child);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("Error accessing child list field", e);
                    }
                })
                .forEach(child -> addChildren(child, dataList));
    }
}

工具类的使用方式

Entity定义

import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class User {

    @TreeCruxField(TreeCruxEnum.ID)
    private Long id;				// 用来定义id字段
    
    @TreeCruxField(TreeCruxEnum.PID)
    private Long pid;				// 用来记录父节点id

    private String name;

    @TreeCruxField(TreeCruxEnum.CHILD_LIST)
    private List<User> sonList;		// 用来保存子集合

    public User(Long id, Long pid, String name) {
        this.id = id;
        this.pid = pid;
        this.name = name;
        this.sonList = new ArrayList<>();
    }

}

MyTest.java

import java.util.ArrayList;
import java.util.List;

public class MyTest {
    
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1L, 0L, "电影"));
        userList.add(new User(1009L, 3L, "亮剑电影"));
        userList.add(new User(2L, 0L, "书"));
        userList.add(new User(3L, 1L, "战争片"));
        userList.add(new User(4L, 1L,"武打片"));
        userList.add(new User(5L, 1L, "爱情片"));
        userList.add(new User(6L, 2L,"战争书"));
        userList.add(new User(193L, 1009L, "亮剑儿子"));
        userList.add(new User(8L, 6L,"亮剑"));
        TreeListUtil<User> treeListUtil = new TreeListUtil(User.class);
        List<User> objects = treeListUtil.buildTree(userList);
        for (User user : objects) {
            printTree(user," ");
        }
    }

    public static void printTree(User user, String prefix) {
        System.out.println(prefix + user.getName());

        for (User child : user.getSonList()) {
            printTree(child, prefix + "-");
        }
    }
    
}

你可以根据具体的需求和数据对象进行配置和使用。TreeListUtil工具类可以让你轻松地处理树状结构的数据,提高了代码的可读性和可维护性。

运行结果

 电影
 -战争片
 --亮剑电影
 ---亮剑儿子
 -武打片
 -爱情片
 书
 -战争书
 --亮剑

TreeListUtil工具类为处理树状结构数据提供了支持,它的简洁API、注解支持和异常处理机制使其成为项目开发中的宝贵工具。无论是处理组织结构、评论回复、分类体系还是其他树状数据,TreeListUtil都能够轻松应对。我们鼓励你在项目中尝试使用TreeListUtil,体验其带来的便利和效率提升。如果你有任何问题或建议,欢迎留言或与我联系。愿TreeListUtil工具类能够在你的项目中发挥作用,让树状结构数据处理变得更加简单和高效。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JAVA新视界

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

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

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

打赏作者

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

抵扣说明:

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

余额充值