Java 万物皆可 Tree 工具类

这是一套可以解决各种类型的数据构建树形结构的解决方案

方案一(推荐)

ITree - 树数据接口

import org.apache.commons.lang.StringUtils;

import java.util.List;

/**
 * 树接口
 *
 * @author: Neo
 * @date: 2021/1/4 14:24
 * @version: 1.0
 */
public interface ITree<T, K> {


    /**
     * 节点ID
     *
     * @return
     */
    K id();


    /**
     * 父节点ID
     *
     * @return
     */
    K parentId();

    /**
     * 设置父节点
     * 
     * @param parent
     */
    default void parent(T parent){
    }

    /**
     * 用于拼接 path 的属性
     * 
     * child.path(parentNode.path() + spliterator + child.pathProperty());
     * 
     * @return
     */
    default String pathProperty(){
        return StringUtils.EMPTY;
    }
    
    /**
     * 获取节点名称
     *
     * @return
     */
    default String path() {
        return StringUtils.EMPTY;
    }

    /**
     * 设置节点名称
     *
     * @param path
     */
    default void path(String path) {
    }

    /**
     * 排序
     *
     * @return
     */
    default Integer index() {
        return 0;
    }

    /**
     * 设置子节点
     *
     * @param children
     */
    default void children(List<T> children) {
    }
}

TreeUtils - 万物皆可 Tree 工具类

import org.apache.commons.collections.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 将扁平数据构造成树结构
 *
 * @author: Neo
 * @date: 2021/1/4 16:49
 * @version: 1.0
 */
public class TreeUtils {

    public static final String DEFAULT_SPLITERATOR = "/";

    private TreeUtils() {
    }


    enum Model {
        TREE, PATH, TREE_AND_PATH
    }


    /**
     * 构造树的入口方法
     *
     * @param originData    数据列表
     * @param rootPredicate 根节点条件
     * @param <T>           implements ITree
     * @param <K>           implements Serializable
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTree(List<T> originData, Predicate<T> rootPredicate) {
        return baseBuild(Model.TREE, originData, null, rootPredicate);
    }


    /**
     * 构建树路径,不改变数据结构
     *
     * @param originData
     * @param rootPredicate
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(List<T> originData, Predicate<T> rootPredicate) {
        return buildPath(originData, DEFAULT_SPLITERATOR, rootPredicate);
    }

    /**
     * 构建树路径,不改变数据结构
     *
     * @param originData
     * @param spliterator
     * @param rootPredicate
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(List<T> originData, CharSequence spliterator, Predicate<T> rootPredicate) {
        return baseBuild(Model.PATH, originData, spliterator, rootPredicate);
    }

    /**
     * 构建树和路径
     *
     * @param originData
     * @param rootPredicate
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTreeAndPath(List<T> originData, Predicate<T> rootPredicate) {
        return buildTreeAndPath(originData, DEFAULT_SPLITERATOR, rootPredicate);
    }

    /**
     * 构建树和路径
     *
     * @param originData
     * @param spliterator
     * @param rootPredicate
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTreeAndPath(List<T> originData, CharSequence spliterator, Predicate<T> rootPredicate) {
        return baseBuild(Model.TREE_AND_PATH, originData, spliterator, rootPredicate);
    }


    /**
     * 树构建的基础方法
     *
     * @param model
     * @param originData
     * @param spliterator
     * @param rootPredicate
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> baseBuild(Model model,
                                                                                    List<T> originData,
                                                                                    CharSequence spliterator,
                                                                                    Predicate<T> rootPredicate) {
        if (Objects.isNull(model) || CollectionUtils.isEmpty(originData) || Objects.isNull(rootPredicate)) {
            return Collections.EMPTY_LIST;
        }

        List<T> result = new ArrayList<>(CollectionUtils.size(originData));

        List<T> roots = originData.stream().filter(rootPredicate).sorted(Comparator.comparing(T::index)).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(roots)) {
            return Collections.EMPTY_LIST;
        }

        // 删除根节点,避免重复遍历
        originData.removeAll(roots);

        switch (model) {
            case TREE:
                roots.forEach(r -> result.add(buildTree(r, originData)));
                break;
            case PATH:
                roots.forEach(r -> result.addAll(buildPath(r, originData, spliterator)));
                break;
            case TREE_AND_PATH:
                roots.forEach(r -> result.add(buildTreeAndPath(r, originData, spliterator)));
                break;
            default:
                throw new RuntimeException();
        }
        return result;
    }


    /**
     * 构建子节点
     *
     * @param parentNode 父节点
     * @param originData 数据列表
     * @param <T>        implements ITree
     * @param <K>        implements Serializable
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> T buildTree(T parentNode, List<T> originData) {
        List<T> childrenNode = new ArrayList<>();

        List<T> children = filterByParentId(parentNode.id(), originData);

        if (CollectionUtils.isNotEmpty(children)) {
            // 删除节点,避免重复遍历
            originData.removeAll(children);
        }
        for (T child : children) {
            child.parent(parentNode);
            childrenNode.add(buildTree(child, originData));
        }
        parentNode.children(childrenNode);
        return parentNode;
    }


    /**
     * 构建子节点路径
     *
     * @param parentNode
     * @param originData
     * @param spliterator
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(T parentNode, List<T> originData, CharSequence spliterator) {
        List<T> result = new ArrayList<>();

        List<T> children = filterByParentId(parentNode.id(), originData);

        result.add(parentNode);

        if (CollectionUtils.isEmpty(children)) {
            return result;
        }

        // 删除节点,避免重复遍历
        originData.removeAll(children);

        for (T child : children) {
            child.parent(parentNode);
            child.path(parentNode.path() + spliterator + child.pathProperty());
            result.addAll(buildPath(child, originData, spliterator));
        }
        parentNode.children(children);
        return result;
    }

    /**
     * 构建树和路径
     *
     * @param parentNode
     * @param originData
     * @param spliterator
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> T buildTreeAndPath(T parentNode, List<T> originData, CharSequence spliterator) {
        List<T> children = filterByParentId(parentNode.id(), originData);

        if (CollectionUtils.isNotEmpty(children)) {
            originData.removeAll(children);
        }

        for (T child : children) {
            child.parent(parentNode);
            child.path(parentNode.path() + spliterator + child.path());
            buildTreeAndPath(child, originData, spliterator);
        }

        parentNode.children(children);
        return parentNode;

    }


    /**
     * @param parentId   父节点ID
     * @param originData 数据列表
     * @param <T>        implements ITree
     * @param <K>        implements Serializable
     * @return
     */
    public static <T extends ITree<T, K>, K extends Serializable> List<T> filterByParentId(K parentId, List<T> originData) {
        return originData.stream()
                .filter(i -> Objects.equals(parentId, i.parentId()))
                .sorted(Comparator.comparing(T::index))
                .collect(Collectors.toList());

    }
}

TestTree - 测试对象

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TestTree implements ITree<TestTree, Long>, Serializable {
    private static final long serialVersionUID = -9153069020164026942L;
    
    private Long id;
    
    private String name;

    private Long parentId;

    private Integer index;

    private List<TestTree> children;


    @Override
    public Long id() {
        return this.id;
    }

    @Override
    public Long parentId() {
        return this.parentId;
    }

    @Override
    public String path() {
        return this.name;
    }

    @Override
    public void path(String path) {
        this.name = path;
    }
    
    @Override
    public Integer index() {
        return this.index;
    }

    @Override
    public void children(List<TestTree> children) {
        this.children = children;
    }
}

TreeUtilsTest - 测试类

import cn.hutool.json.JSONStrFormater;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;

import java.util.List;
import java.util.Objects;

public class TreeUtilsTest {

    public static void main(String[] args) {
        List<TestTree> testTrees;
        System.out.println("=========================【buildTree】============================");
        testTrees = TreeUtils.buildTree(initData(), tree -> Objects.isNull(tree.parentId()));
        System.out.println(JSONStrFormater.format(JSON.toJSONString(testTrees)));
        System.out.println("=========================【buildPath】============================");
        testTrees = TreeUtils.buildPath(initData(), tree -> Objects.isNull(tree.parentId()));
        System.out.println(JSONStrFormater.format(JSON.toJSONString(testTrees)));
        System.out.println("======================【buildTreeAndPath】=========================");
        testTrees = TreeUtils.buildTreeAndPath(initData(), tree -> Objects.isNull(tree.parentId()));
        System.out.println(JSONStrFormater.format(JSON.toJSONString(testTrees)));
    }
    
    static List<TestTree> initData(){
        return Lists.newArrayList(
                TestTree.builder().id(1L).parentId(null).name("一级-1").index(1).build(),
                TestTree.builder().id(2L).parentId(null).name("一级-2").index(2).build(),
                TestTree.builder().id(3L).parentId(null).name("一级-3").index(3).build(),

                TestTree.builder().id(4L).parentId(1L).name("二级-1").index(1).build(),
                TestTree.builder().id(5L).parentId(1L).name("二级-2").index(2).build(),
                TestTree.builder().id(6L).parentId(1L).name("二级-3").index(3).build(),

                TestTree.builder().id(7L).parentId(4L).name("三级-1").index(1).build(),
                TestTree.builder().id(8L).parentId(4L).name("三级-2").index(2).build(),
                TestTree.builder().id(9L).parentId(4L).name("三级-3").index(3).build()
        );
    }
}

测试结果

=========================【buildTree】============================
[
    {
        "children":
        [
            {
                "children":
                [
                    {
                        "children":
                        [
                            
                        ],
                        "id":7,
                        "index":1,
                        "name":"三级-1",
                        "parentId":4
                    },
                    {
                        "children":
                        [
                            
                        ],
                        "id":8,
                        "index":2,
                        "name":"三级-2",
                        "parentId":4
                    },
                    {
                        "children":
                        [
                            
                        ],
                        "id":9,
                        "index":3,
                        "name":"三级-3",
                        "parentId":4
                    }

                ],
                "id":4,
                "index":1,
                "name":"二级-1",
                "parentId":1
            },
            {
                "children":
                [
                    
                ],
                "id":5,
                "index":2,
                "name":"二级-2",
                "parentId":1
            },
            {
                "children":
                [
                    
                ],
                "id":6,
                "index":3,
                "name":"二级-3",
                "parentId":1
            }

        ],
        "id":1,
        "index":1,
        "name":"一级-1"
    },
    {
        "children":
        [
            
        ],
        "id":2,
        "index":2,
        "name":"一级-2"
    },
    {
        "children":
        [
            
        ],
        "id":3,
        "index":3,
        "name":"一级-3"
    }

]
=========================【buildPath】============================
[
    {
        "id":1,
        "index":1,
        "name":"一级-1"
    },
    {
        "id":4,
        "index":1,
        "name":"一级-1/二级-1",
        "parentId":1
    },
    {
        "id":7,
        "index":1,
        "name":"一级-1/二级-1/三级-1",
        "parentId":4
    },
    {
        "id":8,
        "index":2,
        "name":"一级-1/二级-1/三级-2",
        "parentId":4
    },
    {
        "id":9,
        "index":3,
        "name":"一级-1/二级-1/三级-3",
        "parentId":4
    },
    {
        "id":5,
        "index":2,
        "name":"一级-1/二级-2",
        "parentId":1
    },
    {
        "id":6,
        "index":3,
        "name":"一级-1/二级-3",
        "parentId":1
    },
    {
        "id":2,
        "index":2,
        "name":"一级-2"
    },
    {
        "id":3,
        "index":3,
        "name":"一级-3"
    }

]
======================【buildTreeAndPath】=========================
[
    {
        "children":
        [
            {
                "children":
                [
                    {
                        "children":
                        [
                            
                        ],
                        "id":7,
                        "index":1,
                        "name":"一级-1/二级-1/三级-1",
                        "parentId":4
                    },
                    {
                        "children":
                        [
                            
                        ],
                        "id":8,
                        "index":2,
                        "name":"一级-1/二级-1/三级-2",
                        "parentId":4
                    },
                    {
                        "children":
                        [
                            
                        ],
                        "id":9,
                        "index":3,
                        "name":"一级-1/二级-1/三级-3",
                        "parentId":4
                    }

                ],
                "id":4,
                "index":1,
                "name":"一级-1/二级-1",
                "parentId":1
            },
            {
                "children":
                [
                    
                ],
                "id":5,
                "index":2,
                "name":"一级-1/二级-2",
                "parentId":1
            },
            {
                "children":
                [
                    
                ],
                "id":6,
                "index":3,
                "name":"一级-1/二级-3",
                "parentId":1
            }

        ],
        "id":1,
        "index":1,
        "name":"一级-1"
    },
    {
        "children":
        [
            
        ],
        "id":2,
        "index":2,
        "name":"一级-2"
    },
    {
        "children":
        [
            
        ],
        "id":3,
        "index":3,
        "name":"一级-3"
    }

]

方案二

BuildTreeUtils

import org.apache.commons.collections.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * 构造树
 *
 * @author: Neo
 * @date: 2020/12/24 17:26
 * @version: 1.0
 */
public class BuildTreeUtils {


    /**
     * @param treeList  数据列表
     * @author: Neo
     * @aate: 2020/12/24 17:27
     * @version: 1.0
     */
    public static <T extends BaseTree<T>> List<T> buildTree(List<T> treeList) {

        List<T> firstNodeList = buildChildren(0L, treeList);
        if (CollectionUtils.isEmpty(firstNodeList)) {
            return Collections.EMPTY_LIST;
        }

        List<T> result = new ArrayList<>();
        for (T node : firstNodeList) {
            T n = buildChildren(node, treeList);
            result.add(n);
        }
        return result;
    }

    public static <T extends BaseTree<T>> T buildChildren(T node, List<T> list) {
        List<T> childrenNode = new ArrayList<>();
        List<T> children = buildChildren(node.getId(), list);
        for (T child : children) {
            childrenNode.add(buildChildren(child, list));
        }
        node.setChildren(childrenNode);
        return node;
    }


    public static <T extends BaseTree<T>> List<T> buildChildren(Long parentId, List<T> list) {
        return list.stream()
                .filter(i -> Objects.equals(parentId, i.getParentId()))
                .sorted(Comparator.comparing(T::getIndex))
                .collect(Collectors.toList());

    }

}

BaseTree

import cn.hutool.core.convert.Convert;
import lombok.Data;

import java.io.Serializable;
import java.util.List;
import java.util.Objects;

@Data
public class BaseTree<T> implements Serializable {
    private static final long serialVersionUID = -4881070396277928428L;

    /**
     * 主键ID
     */
    private Long id;

    /**
     * 父编号
     */
    private Long parentId;

    private Integer viewIndex;

    private List<T> children;

    public Integer getIndex() {
        return Objects.nonNull(this.viewIndex) ? this.viewIndex : Convert.toInt(this.id);
    }
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值