Java树迭代器(支持前序、后序和层级遍历)

1、设计背景

为简化实际业务场景中常见的菜单、组织架构等树型数据结构的处理,避免代码中出现相似代码和复杂代码,采用迭代器模式,设计一套树迭代器,支持业务场景中常用的前序遍历、后序遍历和层级遍历。

2、UML图

在这里插入图片描述

3、使用demo

1、SimpleTree

@Data
public class SimpleTree {

    /**
     * 节点名称
     */
    private String nodeName;

    /**
     * 子节点集合
     */
    private List<SimpleTree> children;
}

2、IteratorClient

public class IteratorClient {

    public static void main(String[] args) {
        Tree<SimpleTree> tree = new Tree<>(initTree(), SimpleTree::getChildren, SimpleTree::setChildren);
        TreeIterator<SimpleTree> iterator = tree.iterator(Tree.TreeIteratorEnum.LEVEL);
        while (iterator.hasNext()) {
            SimpleTree item = iterator.next();
            System.out.println(item.getNodeName());
            // 删除当前节点
            // iterator.remove();
            // 获取当前节点的父节点
            // SimpleTree currentParentNode = iterator.getCurrentParentNode();
            // 获取当前节点的子节点
            // List<SimpleTree> children = item.getChildren();
        }
    }

    /**
     * 组装树
     */
    private static List<SimpleTree> initTree() {
        List<SimpleTree> result = new ArrayList<>();
		// ... 填充树节点
        return result;
    }
}

4、树迭代器源码

1、TreeIterator

/**
 * 树迭代器-基础类
 *
 * @author mrdarren
 * @since 2021/7/8
 */
public abstract class TreeIterator<T> implements Iterator<T> {

    /**
     * 当前节点
     */
    T currentNode;

    /**
     * 当前父节点
     */
    T currentParentNode;

    /**
     * 子节点查询方法
     */
    Function<T, List<T>> getChildrenFun;

    TreeIterator(Function<T, List<T>> getChildrenFun) {
        this.getChildrenFun = getChildrenFun;
    }

    public T getCurrentParentNode() {
        return currentParentNode;
    }

    /**
     * 移除节点
     *
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public void remove() {
        // 判断当前父节点是否已取出
        if (this.currentParentNode != null) {
            // 获取子节点集合
            List<T> nodeList = getChildrenFun.apply(this.currentParentNode);
            // 移除当前子节点
            if (CollectionUtil.isNotEmpty(nodeList)) {
                nodeList.remove(this.currentNode);
            }
        }
    }
}

2、TreeLevelIterator

/**
 * 树-层级遍历迭代器
 *
 * @author mrdarren
 * @since 2021/7/8
 */
public class TreeLevelIterator<T> extends TreeIterator<T> {

    /**
     * 迭代队列
     */
    private Deque<T> queue = new LinkedList<>();

    /**
     * 父节点队列
     */
    private Deque<T> parentQueue = new LinkedList<>();

    public TreeLevelIterator(Function<T, List<T>> getChildrenFun, T root) {
        super(getChildrenFun);
        pushQueue(root);
    }

    /**
     * 压节点入队列
     *
     * @param root 父节点
     * @author mrdarren
     * @since 2021/7/8
     */
    private void pushQueue(T root) {
        if (root == null) {
            return;
        }
        List<T> nodeList = this.getChildrenFun.apply(root);
        if (CollectionUtil.isEmpty(nodeList)) {
            return;
        }
        nodeList.forEach(item -> {
            this.queue.add(item);
            this.parentQueue.add(root);
        });
    }

    /**
     * 是否有下一个节点
     *
     * @return {@link boolean}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public boolean hasNext() {
        return !this.queue.isEmpty() && !this.parentQueue.isEmpty();
    }

    /**
     * 查询下一个节点
     *
     * @return {@link T}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        // pop双队列中第一个节点
        this.currentNode = this.queue.pop();
        this.currentParentNode = this.parentQueue.pop();

        // 子节点入队列
        pushQueue(this.currentNode);
        return this.currentNode;
    }
}

3、TreePostIterator

/**
 * 树-后序遍历迭代器
 * 遍历一次树,将前序遍历顺序存入队列用于外部迭代
 *
 * @author mrdarren
 * @since 2021/7/8
 */
public class TreePostIterator<T> extends TreeIterator<T> {

    /**
     * 迭代队列
     */
    private Deque<T> queue = new LinkedList<>();

    /**
     * 父节点队列
     */
    private Deque<T> parentQueue = new LinkedList<>();

    public TreePostIterator(Function<T, List<T>> getChildrenFun, T root) {
        super(getChildrenFun);
        initQueue(root);
    }

    /**
     * 是否有下一个节点
     *
     * @return {@link boolean}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public boolean hasNext() {
        return !this.queue.isEmpty() && !this.parentQueue.isEmpty();
    }

    /**
     * 查询下一个节点
     *
     * @return {@link T}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        // pop双队列中的第一个节点
        this.currentNode = this.queue.pop();
        this.currentParentNode = this.parentQueue.pop();
        return this.currentNode;
    }

    /**
     * 初始化队列
     *
     * @param root 根节点
     * @author mrdarren
     * @since 2021/7/8
     */
    private void initQueue(T root) {
        if (root == null) {
            return;
        }

        List<T> nodeList = this.getChildrenFun.apply(root);
        if (CollectionUtil.isEmpty(nodeList)) {
            return;
        }

        // 创建临时栈,将二级节点入栈
        Stack<T> stack = new Stack<>();
        Stack<T> parentStack = new Stack<>();
        nodeList.forEach(item -> {
            stack.push(item);
            parentStack.push(root);
        });

        // 遍历树
        while (!stack.isEmpty()) {
            // pop子节点和对应的父节点
            T node = stack.pop();
            T parentNode = parentStack.pop();
            // 子节点和父节点入队列
            this.queue.push(node);
            this.parentQueue.push(parentNode);

            List<T> subNodeList = this.getChildrenFun.apply(node);
            if (CollectionUtil.isNotEmpty(subNodeList)) {
                subNodeList.forEach(item -> {
                    stack.push(item);
                    parentStack.push(node);
                });
            }
        }
    }
}

4、TreePreIterator

/**
 * 树-前序遍历迭代器
 *
 * @author mrdarren
 * @since 2021/7/8
 */
public class TreePreIterator<T> extends TreeIterator<T> {

    /**
     * 迭代栈
     */
    private Stack<T> stack = new Stack<>();

    /**
     * 父节点栈
     */
    private Stack<T> parentStack = new Stack<>();

    public TreePreIterator(Function<T, List<T>> getChildrenFun, T root) {
        super(getChildrenFun);
        pushChildrenToStack(root);
    }

    /**
     * 是否有下一个节点
     *
     * @return {@link boolean}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public boolean hasNext() {
        return !this.stack.isEmpty() && !this.parentStack.isEmpty();
    }

    /**
     * 查询下一个节点
     *
     * @return {@link T}
     * @author mrdarren
     * @since 2021/7/8
     */
    @Override
    public T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        // pop双栈中最后一个节点
        this.currentNode = this.stack.pop();
        this.currentParentNode = this.parentStack.pop();

        // 子节点入栈
        pushChildrenToStack(this.currentNode);
        return this.currentNode;
    }

    /**
     * 入栈
     *
     * @param node 节点
     * @author mrdarren
     * @since 2021/7/8
     */
    private void pushChildrenToStack(T node) {
        if (node == null) {
            return;
        }
        // 查询子节点
        List<T> nodeList = this.getChildrenFun.apply(node);
        if (CollectionUtil.isEmpty(nodeList)) {
            return;
        }
        // 先压右子节点,再压左子节点
        for (int i = nodeList.size() - 1; i >= 0; i--) {
            // 子节点入栈
            this.stack.push(nodeList.get(i));
            // 子节点对应的父节点入栈
            this.parentStack.push(node);
        }
    }
}

5、Tree

/**
 * 树对象
 *
 * @author mrdarren
 * @since 2021/7/8
 */
public class Tree<T> {

    /**
     * 根节点集合
     */
    private T root;

    /**
     * 子节点查询方法
     */
    private Function<T, List<T>> getChildrenFun;

    /**
     * 构造方法,将树转为统一根对象
     *
     * @param rootList       根节点集合
     * @param getChildrenFun 获取子节点方法
     * @param setChildrenFun 设置子节点方法
     * @author mrdarren
     * @since 2021/7/8
     */
    @SuppressWarnings("unchecked")
    public Tree(List<T> rootList, Function<T, List<T>> getChildrenFun, BiConsumer<T, List<T>> setChildrenFun) {
        try {
            this.getChildrenFun = getChildrenFun;
            if (CollectionUtil.isNotEmpty(rootList)) {
                T firstRoot = rootList.get(0);
                T newRoot = (T) firstRoot.getClass().newInstance();
                setChildrenFun.accept(newRoot, rootList);
                this.root = newRoot;
            } else {
                this.root = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassCastException();
        }
    }

    /**
     * 根据类型返回对应的迭代器
     *
     * @param treeIteratorEnum 迭代器类型
     * @return {@link TreeIterator}
     * @author mrdarren
     * @since 2021/7/8
     */
    public TreeIterator<T> iterator(TreeIteratorEnum treeIteratorEnum) {
        TreeIterator<T> iterator;
        switch (treeIteratorEnum) {
            case PRE:
                iterator = new TreePreIterator<>(this.getChildrenFun, this.root);
                break;
            case LEVEL:
                iterator = new TreeLevelIterator<>(this.getChildrenFun, this.root);
                break;
            case POST:
                iterator = new TreePostIterator<>(this.getChildrenFun, this.root);
                break;
            default:
                iterator = new TreePreIterator<>(this.getChildrenFun, this.root);
                break;
        }
        return iterator;
    }

    /**
     * 迭代器类型枚举
     *
     * @author mrdarren
     * @since 2021/7/8
     */
    public enum TreeIteratorEnum {
        /**
         * 前序遍历
         */
        PRE,
        /**
         * 后序遍历
         */
        POST,
        /**
         * 层级遍历
         */
        LEVEL
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值