当前位置: 首页 > news >正文

Java树结构通用工具类

Java树结构通用工具类

    • Maven依赖
    • TreeUtil

可进行树的生成,节点查找,根据规则剪枝操作。

Maven依赖

	<dependencies>
        <dependency>
            <groupId>io.vavr</groupId>
            <artifactId>vavr</artifactId>
            <version>0.10.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
	</dependencies>

TreeUtil

import io.vavr.collection.Array;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class TreeUtil {

    public static abstract class TreeNode<T> {

        private transient TreeNode<T> parent;

        /**
         * 树节点id
         */
        public abstract T id();

        /**
         * 获取该节点的父节点id
         */
        public abstract T parentId();

        /**
         * 是否是根节点
         */
        public abstract boolean root();

        /**
         * 设置节点的子节点列表
         */
        public abstract void putChildren(List<? extends TreeNode<T>> children);

        public abstract List<? extends TreeNode<T>> takeChildren();

        protected void putParent(TreeNode<T> parent) {
            this.parent = parent;
        }

        protected TreeNode<T> takeParent() {
            return this.parent;
        }
    }


    /**
     * 根据所有树节点列表,生成含有所有树形结构的列表
     */
    public static <E extends TreeNode<T>, T> List<E> generateTrees(List<E> nodes) {
        List<E> roots = new ArrayList<>();
        for (Iterator<E> item = nodes.iterator(); item.hasNext(); ) {
            E node = item.next();
            //如果是根节点
            if (node.root()) {
                roots.add(node);
                //从所有节点列表中删除该节点,以免后续重复遍历该节点
                item.remove();
            }
        }
        roots.forEach(e -> setChildren(e, nodes));
        return roots;
    }



    /**
     * 根据规则筛选树(减枝,并保留父节点)
     */
    @SuppressWarnings("unchecked")
    public static <E extends TreeNode<T>, T> List<E> selectByRule(List<E> nodes, Predicate<E> rule) {
        if (CollectionUtils.isEmpty(nodes)) {
            return Collections.emptyList();
        }
        LinkedList<E> linkedList = new LinkedList<>(nodes);
        List<E> list = new ArrayList<>();
        E e;
        while ((e = linkedList.poll()) != null) {
            List<? extends TreeNode<T>> children = e.takeChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                for (TreeNode<T> child : children) {
                    child.putParent(e);
                    linkedList.offer((E) child);
                }
            }
            if (rule.test(e)) {
                list.addAll(findParents(e));
            }
        }
        Array<E> array = Array.ofAll(list).distinctBy(TreeNode::id);
        array.forEach(ele -> ele.putChildren(null));
        return generateTrees(array.asJavaMutable());
    }



    /**
     * 深度优先遍历树
     */
    @SuppressWarnings("unchecked")
    public static <E extends TreeNode<T>, T> void dfsTree(E e, Consumer<E> action) {
        if (e != null) {
            action.accept(e);
            List<? extends TreeNode<T>> children = e.takeChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                for (TreeNode<T> child : children) {
                    dfsTree((E) child, action);
                }
            }
        }
    }

    /**
     * 广度优先遍历树,action为(父节点,子节点)的操作,用于处理树节点之间的关系
     */
    @SuppressWarnings("unchecked")
    public static <E extends TreeNode<T>, T> void bfsTree(E e, BiConsumer<E, E> action) {
        LinkedList<E> linkedList = new LinkedList<>();
        linkedList.add(e);
        E temp;
        while ((temp = linkedList.poll()) != null) {
            List<? extends TreeNode<T>> children = temp.takeChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                for (TreeNode<T> child : children) {
                    action.accept(temp, (E) child);
                    linkedList.offer((E) child);
                }
            }
        }
    }

    /**
     * 根据id查找树节点
     */
    @SuppressWarnings("unchecked")
    public static <E extends TreeNode<T>, T> E findNodeById(E node, T id) {
        if (node != null) {
            T nodeId = node.id();
            if (Objects.equals(nodeId, id)) {
                return node;
            }
            List<? extends TreeNode<T>> children = node.takeChildren();
            if (CollectionUtils.isNotEmpty(children)) {
                for (TreeNode<T> child : children) {
                    E nd = (E) findNodeById(child, id);
                    if (nd != null) {
                        return nd;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 从所有节点列表中查找并设置parent的所有子节点
     *
     * @param parent 父节点
     * @param nodes  所有树节点列表
     */
    private static <E extends TreeNode<T>, T> void setChildren(E parent, List<E> nodes) {
        List<E> children = new ArrayList<>();
        Object parentId = parent.id();

        for (Iterator<E> iterator = nodes.iterator(); iterator.hasNext(); ) {
            E node = iterator.next();
            if (Objects.equals(node.parentId(), parentId)) {
                children.add(node);
                //从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        //如果孩子为空,则直接返回,否则继续遍历递归设置孩子的孩子
        if (children.isEmpty()) {
            return;
        }
        parent.putChildren(children);
        children.forEach(e -> setChildren(e, nodes));

    }

    @SuppressWarnings("unchecked")
    private static <E extends TreeNode<T>, T> List<E> findParents(E e) {
        List<E> list = new ArrayList<>();
        list.add(e);
        E temp = e;
        while ((temp = (E) temp.takeParent()) != null) {
            list.add(temp);
        }
        return list;
    }
}

相关文章:

  • Spark基础知识
  • Google Chrome Canary版官方下载及安装教程【适用于开发者与进阶用户】
  • SpringMVC基础二(RestFul、接收数据、视图跳转)
  • Java 中 boolean 类型,几个byte?
  • 服务器信息收集
  • Flink回撤流详解 代码实例
  • 如何关闭MacOS中鼠标滚轮滚动加速
  • Mybatis Plus与SpringBoot的集成
  • 【游戏安全】基于协议时序的挖掘
  • Linux网络编程——TCP通信的四次挥手
  • 《Vue Router实战教程》13.导航守卫
  • ip route show 命令详解
  • Android Input——InputDispatcher分发事件(六)
  • JAVA并发编程高级--读写锁 ReentrantReadWriteLock 的原理
  • 如何在 Spring Boot 项目中使用 MyBatis 进行批量操作以提升性能?
  • .NET-EFCore基础知识
  • Git的简介和简单的命令使用介绍
  • ERC-20 代币标准
  • 21 天 Python 计划:使用SQLAlchemy 中的ORM查询
  • 【专题】搜索题型(BFS+DFS)
  • 推广网站怎么做模版/站长推荐
  • 可视化网站建设软件/域名注册价格及续费
  • 物流网站做代理/seo的优化流程
  • 帝国cms如何做电影网站/站长工具天美传媒
  • 网站推广怎么做 知乎/怎么引流客源最好的方法
  • 收购域名/长沙关键词优化平台