All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.iwuyc.tools.commons.basic.collections.NavigateTree Maven / Gradle / Ivy

The newest version!
/*
 * 深圳市灵智数科有限公司版权所有.
 */
package com.iwuyc.tools.commons.basic.collections;

import com.iwuyc.tools.commons.util.collection.CollectionUtil;
import lombok.Getter;
import lombok.ToString;

import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.IntConsumer;

/**
 * 可快速访问的树形接口
 *
 * @author Neil
 * @version 1.0.0
 * @since 2022.1
 */
@ToString
public class NavigateTree extends HashMap> implements Serializable {
    private static final long serialVersionUID = -1475220629986796510L;

    private final Collection rootNodePrimaryKeys = new HashSet<>();

    public NavigateTree(int capacitySize) {
        super(capacitySize);
    }

    public NavigateTree() {
    }

    public static  NavigateTree empty() {
        return new NavigateTree<>(0);
    }

    private static  void scoreSetterPush(final Deque> stack, final NavigateTreeNode node) {
        stack.push(ScoreSetter.createRight(node));
        stack.push(ScoreSetter.createLeft(node));
    }

    public void add(NavigateTreeNode node) {

        final Deque> nodeStack = new ArrayDeque<>();
        nodeStack.push(node);
        while (!nodeStack.isEmpty()) {
            final NavigateTreeNode item = nodeStack.pop();
            this.add(item.getParentKey(), item.getPrimaryKey(), item.getVal());
            if (item.hasChildren()) {
                nodeStack.addAll(item.getChildren().values());
            }
        }
    }

    /**
     * Add root node into navigate tree
     *
     * @param primaryKey primary key
     * @param val        data
     */
    public void add(K primaryKey, V val) {
        this.add(null, primaryKey, val);
    }

    /**
     * Add normal node or root node(if parent key was null) into navigate tree
     *
     * @param parentKey  parent key,the primary key was root key if null.
     * @param primaryKey primary key
     * @param val        data
     */
    public void add(K parentKey, K primaryKey, V val) {
        final NavigateTreeNode currentNode = super.computeIfAbsent(primaryKey, key -> NavigateTreeNode.createNode(parentKey, primaryKey));
        currentNode.setVal(val);

        if (null == parentKey) {
            rootNodePrimaryKeys.add(currentNode.getPrimaryKey());
        } else {
            final NavigateTreeNode parenNode = super.get(parentKey);
            if (null != parenNode) {
                parenNode.addChild(currentNode);
            } else {
                // create virtual node for parent and put into root nodes
                final NavigateTreeNode virtualParentNode = NavigateTreeNode.createNode(null, parentKey);
                super.put(parentKey, virtualParentNode);
                virtualParentNode.addChild(currentNode);
                rootNodePrimaryKeys.add(virtualParentNode.getPrimaryKey());
                rootNodePrimaryKeys.remove(currentNode.getPrimaryKey());
            }
        }
    }

    public boolean remove(K primaryKey) {
        NavigateTreeNode removeNode = super.remove(primaryKey);
        if (null == removeNode) {
            return false;
        }
        this.rootNodePrimaryKeys.remove(removeNode.getPrimaryKey());
        // remove all child from this container
        for (K childKey : removeNode.getAllChildrenPrimaryKey()) {
            super.remove(childKey);
        }

        return true;
    }

    public Optional> get(K primaryKey) {
        return Optional.ofNullable(super.get(primaryKey));
    }

    public void addAll(NavigateTree otherNavigateTree) {
        if (null == otherNavigateTree) {
            return;
        }
        this.addAll(otherNavigateTree.values());
    }

    public void addAll(Collection> navigateTreeNodes) {
        if (CollectionUtil.isEmpty(navigateTreeNodes)) {
            return;
        }
        for (NavigateTreeNode otherItem : navigateTreeNodes) {
            this.add(otherItem);
        }
    }

    /**
     * 左右节点分数计算,只计算root节点下的所有子节点
     *
     * @param scoreStart 分数起始值
     */
    public void scoreCalc(final int scoreStart) {
        this.scoreCalc(null, scoreStart);
    }

    /**
     * 左右节点分数计算,只计算root节点下的所有子节点
     *
     * @param sorted     兄弟节点(同一父结点的值)排序规则
     * @param scoreStart 分数起始值
     */
    public void scoreCalc(final Comparator> sorted, int scoreStart) {
        for (NavigateTreeNode rootNode : this.getRootNodes()) {
            this.scoreCalc(rootNode, sorted, scoreStart);
        }
    }

    /**
     * 左右节点分数计算
     *
     * @param rootNode   根节点实例
     * @param sorted     排序规则
     * @param scoreStart 分数起始点
     */
    private void scoreCalc(NavigateTreeNode rootNode, Comparator> sorted, int scoreStart) {
        final Deque> stack = new ArrayDeque<>();
        scoreSetterPush(stack, rootNode);
        int counter = scoreStart;
        while (!stack.isEmpty()) {
            final ScoreSetter scoreSetter = stack.pop();
            scoreSetter.setScore(counter++);
            if (!scoreSetter.hasChildren()) {
                continue;
            }

            final NavigateTreeNode current = scoreSetter.getNode();
            final Map> childrenByKey = current.getChildren();

            final Collection> children;
            if (null != sorted) {
                children = new ArrayList<>(childrenByKey.values());
                // 优先级高的要先处理,因此,要将提供的comparator进行reversed
                ((List>) children).sort(sorted.reversed());
            } else {
                children = childrenByKey.values();
            }

            for (NavigateTreeNode child : children) {
                scoreSetterPush(stack, child);
            }
        }
    }

    /**
     * 获取根节点列表
     *
     * @return 所有的根节点列表
     */
    public Collection> getRootNodes() {
        if (CollectionUtil.isEmpty(rootNodePrimaryKeys)) {
            return Collections.emptySet();
        }
        final int size = rootNodePrimaryKeys.size();
        final Collection> result = new HashSet<>(size);
        for (K k : rootNodePrimaryKeys) {
            final NavigateTreeNode node = super.get(k);
            result.add(node);
        }
        return result;
    }

    private static class ScoreSetter {
        private final boolean left;
        private final IntConsumer scoreSetterMethod;
        @Getter
        private final NavigateTreeNode node;

        private ScoreSetter(boolean left, IntConsumer scoreSetterMethod, NavigateTreeNode node) {
            this.left = left;
            this.scoreSetterMethod = scoreSetterMethod;
            this.node = node;
        }

        public static  ScoreSetter createLeft(NavigateTreeNode node) {
            return new ScoreSetter<>(true, node::setLeft, node);
        }

        public static  ScoreSetter createRight(NavigateTreeNode node) {
            return new ScoreSetter<>(false, node::setRight, node);
        }

        public void setScore(int score) {
            this.scoreSetterMethod.accept(score);
        }

        public boolean hasChildren() {
            return this.left && this.node != null && this.node.hasChildren();
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }
        NavigateTree that = (NavigateTree) o;
        return Objects.equals(rootNodePrimaryKeys, that.rootNodePrimaryKeys);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), rootNodePrimaryKeys);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy