java 通用扁平数据转换成树形结构

大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)

所谓树形结构,就是上级节点中,包含若干子节点,然后子节点中又包含其子节点,一般是没有层级次数限制。

数据库中又是按扁平结构存储的,因此在使用的时候,必须转换成树数据结构才行!

部门树、权限树、菜单树,好麻烦哦!一不小心就递归死循环了 (ಡωಡ)


形成树最少的条件:

  • 节点必须有自己的id,并且是唯一的 id
  • 节点必须有上级节点id parentId
  • 节点的id,和parentId不能相同
  • 节点的parentId,一定是属于其他节点的id

满足上述四个条件,就可以形成树了~


Nodeable

/**
 * 树节点对象,必须实现这个interface,并且实现theId、theParentId方法!
 * 分别返回节点的id,和父节点id
 */
public interface Nodeable {
    /**
     * 返回当前节点id
     * */
    public String theId();
    /**
     * 返回当前节点父节点id
     * */
    public String theParentId();
}

NodeInfo

import java.util.LinkedList;
import java.util.List;

public class NodeInfo<T extends Nodeable> implements Cloneable{

    static String rootId = "@ROOT";   //根节点id
    static String rootParentId = "";   //根节点的parent
    static int rootLevel = 0;   //根节点的级别


    /**
     * 默认根节点
     * */
    static NodeInfo def = new NodeInfo();
    static  {
        def.cont = new Nodeable() {
            @Override
            public String theId() {
                return rootId;
            }

            @Override
            public String theParentId() {
                return rootParentId;
            }
        };
        def.level = rootLevel;
        def.id = rootId;
        def.parentId = rootParentId;
        def.parentIds = rootParentId;
        def.children = new LinkedList<>();
    };


    /**
     * 当前元素
     * */
    private T cont;
    private String id;        //当前元素id
    private String parentId;  //当前元素父节点id
    private List<NodeInfo<T>> children;   //所有的下级节点


    private int level;  //从根节点开始计算,第n级节点
    private String parentIds;
    private String slipt = ",";  //节点所有parentIds的分割符



    NodeInfo(T cont) throws Exception{
        if(cont == null || TreeHandler.isBlank(cont.theId())){
            throw new Exception("元素或者元素id不能为空");
        }
        this.cont = cont;
        this.id = TreeHandler.trimToEmpty(cont.theId());
        this.parentId = TreeHandler.trimToEmpty(cont.theParentId());
    }
    private NodeInfo(){}



    @Override
    public NodeInfo<T> clone(){
        try { return (NodeInfo<T>) super.clone(); } catch ( CloneNotSupportedException e ) { }
        return null;
    }

    public T getCont() {
        return cont;
    }


    public NodeInfo<T> putParent(NodeInfo<T> parent, int level, NodeInfo<T> root){
        this.level = level;
        this.parentId = parent.id;
        this.parentIds = (parent.getParentIds() + root.slipt + parent.getId()).replaceFirst("^" + root.slipt, "");
        return this;
    }
    public void putChild(NodeInfo<T> child){
        if( children == null ){
            children = new LinkedList<>();
        }
        children.add(child);
    }


    /**
     * 设置分割符
     * */
    void setSlipt(String slipt) {
        this.slipt = slipt ;
    }
    String getSlipt() {
        return slipt;
    }

    public void setCont(T cont) {
        this.cont = cont;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getParentId() {
        return parentId;
    }

    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

    public String getParentIds() {
        return parentIds;
    }

    public void setParentIds(String parentIds) {
        this.parentIds = parentIds;
    }

    public List<NodeInfo<T>> getChildren() {
        return children;
    }

    public void setChildren(List<NodeInfo<T>> children) {
        this.children = children;
    }
}

TreeHandler

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class TreeHandler<T extends Nodeable> {


    /****************************************************/

    private Map<String, NodeInfo<T>> nodeMap;



    /**
     * 根节点
     * */
    private NodeInfo<T> root;



    private TreeHandler(){}


    /**
     * @param root 根节点对象
     * */
    public TreeHandler(NodeInfo<T> root){
        this.root = root;
        this.root.setId(root.getId());
        this.root.setParentId(root.getParentId());
        this.root.setParentIds(root.getParentIds());
        this.root.setSlipt(root.getSlipt());
    }

    /**
     * @param element   根节点元素
     * @param rootParentId  根节点的上级id
     * @param slipt     元素所有上级id分割符
     */
    public TreeHandler(T element, String rootParentId, String slipt) throws Exception{
        this.root = creatNode(element);
        this.root.setId(element.theId());
        this.root.setParentId(element.theParentId());
        this.root.setParentIds(rootParentId);
        this.root.setSlipt(slipt);
    }

    /**
     * @param rootId 根节点的id,注意,这样会在nodeMap中存入一个虚拟的Nodeable节点,
     *  在后续遍历nodes时,会可能造成类型转换异常
     * */
    public TreeHandler(String rootId){
        this(rootId, ",");
    }

    /**
     * @param rootId 根节点的id,注意,这样会在nodeMap中存入一个虚拟的Nodeable节点,
     *  在后续遍历nodes时,会可能造成类型转换异常
     * @param slipt 元素所有上级id分割符,默认","
     * */
    public TreeHandler(String rootId, String slipt){
        this(rootId, "", slipt);
    }

    /**
     * @param rootId 根节点的id
     * @param rootParentId 根节点的上级id,默认""
     * @param slipt 元素所有上级id分割符,默认","
     * */
    public TreeHandler(String rootId, String rootParentId, String slipt){
        this.root = NodeInfo.def.clone();
        this.root.setId(rootId);
        this.root.setParentId(rootParentId);
        this.root.setParentIds(rootParentId);
        this.root.setSlipt(slipt);
    }


    /**
     * 解析,将扁平数据解析成树状
     * @param elements 待处理集合
     * */
    public NodeInfo<T> parseArray(List<T> elements) throws Exception{
        if( TreeHandler.isNotEmpty(elements) ){
            nodeMap = new HashMap<>((int)(elements.size() * 1.34));
            nodeMap.put(root.getId(), root);
            Map<String, List<NodeInfo<T>>> parentIdMap = this.groupByParentId(elements);    //按parentId分组后的数据
            List<NodeInfo<T>> firsts = parentIdMap.get(root.getId());
            root.setChildren(firsts);
            for( NodeInfo<T> first : firsts){
                first.putParent(root, root.getLevel(), root);
                parse(first, root, root.getLevel(), parentIdMap);
            }
        }
        return root;
    }


    /**
     * 解析
     * @param curNode 当前节点
     * @param parentNode 上级节点
     * @param level 当前节点所在级别                
     * */
    private void parse(NodeInfo<T> curNode, NodeInfo<T> parentNode, int level, Map<String, List<NodeInfo<T>>> parentIdMap){

        level ++ ;

        List<NodeInfo<T>> curNodeChildren = parentIdMap.get(curNode.getId());   //得到当前节点的子节点

        curNode.putParent(parentNode, parentNode.getLevel(), root);
        curNode.setChildren(curNodeChildren);
        curNode.setLevel(level);

        if( TreeHandler.isEmpty(curNodeChildren) ){//当前节点没有子节点了
            return;
        }
        //当前节点有子节点
        for( NodeInfo<T> child : curNodeChildren ){
            parse(child, curNode, level, parentIdMap);
        }
        return;
    }






    /**
     * 根据节点id,从树上,剪掉此节点、以及其后代节点
     * */
    public NodeInfo<T> cutNodeById(String nodeId){
        NodeInfo<T> cutNode = queryTree(nodeId);
        if( cutNode != null ){
            NodeInfo<T> parentNode = queryTree(cutNode.getParentId());
            cutNode(cutNode);
            List<NodeInfo<T>> children = parentNode.getChildren();
            int i = 0;
            for(NodeInfo<T> child : children){
                if( cutNode.getId().equals(child.getId()) ){
                    break;
                }
                i ++ ;
            }
            children.remove(i);
        }
        return cutNode;
    };


    /**
     * 根据节点id,分离成一个子树
     * */
    public TreeHandler splitById(String nodeId, String rootParentId) throws Exception {
        return splitById(nodeId, rootParentId, ",");
    }
    public TreeHandler splitById(String nodeId, String rootParentId, String slipt) throws Exception {
        TreeHandler handler = new TreeHandler();

        if( nodeMap.containsKey(nodeId) ){
            NodeInfo<T> tree = cutNodeById(nodeId);
            handler.root = tree;
            handler.root.setId(tree.getId());
            handler.root.setParentId(tree.getParentId());
            handler.root.setParentIds(rootParentId);
            handler.root.setSlipt(slipt);

            List<T> elements = new LinkedList<>();
            getNodes(tree, elements);

            handler.root.getChildren().clear();
            handler.parseArray(elements);

        }
        return handler;
    };

    private void getNodes(NodeInfo<T> node, List<T> elements){
        List<NodeInfo<T>> children = node.getChildren();
        if( TreeHandler.isEmpty(children) ){
            elements.add(node.getCont());
            return;
        }
        for ( NodeInfo<T> child : children ){
            getNodes(child, elements);
        }
        elements.add(node.getCont());
    }



    /**
     * @param node 枝剪某个节点
     * */
    private void cutNode(NodeInfo<T> node){
        List<NodeInfo<T>> children = node.getChildren();
        if( TreeHandler.isEmpty(children) ){//如果没有子节点了
            nodeMap.remove(node.getId());
            return;
        }
        for(NodeInfo<T> child : children){
            cutNode(child);
        }
        nodeMap.remove(node.getId());
    }





    /**
     * 得到所有的节点
     * */
    public List<NodeInfo<T>> getNodes(){
        Collection<NodeInfo<T>> values = nodeMap.values();
        List<NodeInfo<T>> list = new ArrayList<>(values.size());
        for( NodeInfo<T> o : values ){
            list.add(o);
        }
        return list;
    }
    
    /**
     * 得到Map: 节点id -> 节点 
     * */
    public Map<String, NodeInfo<T>> getNodeMap(){
        return nodeMap;
    }

    /**
     * 创建节点
     * */
    public NodeInfo creatNode(T element) throws Exception {
        NodeInfo<T> node = null;
        if( nodeMap != null ){
            node = nodeMap.get(element.theId());
        }
        if( node == null ){
            node = new NodeInfo(element);
        }
        //如果元素没有传入上级节点,按一级节点
        if( TreeHandler.isBlank(node.getParentId()) ){
            node.setParentId(root.getId());
        }
        return node;
    }



    /**
     * 遍历树,并且返回该节点
     * */
    public NodeInfo<T> queryTree(String nodeId){
        NodeInfo<T> node = nodeMap.get(nodeId);
        return node;
    }



    /**
     * 遍历,找到上级节点
     * */
    private NodeInfo<T> foreach(List<NodeInfo<T>> children, String seachId){
        NodeInfo<T> node = null;
        if( TreeHandler.isNotEmpty(children) ){
            for( NodeInfo child : children ){
                System.out.println(child.getId());
                if( seachId.equals(child.getId()) ){
                    return child;
                }
                node = foreach(child.getChildren(), seachId);
                if(node != null){
                    return node;
                }
            }
            return null;
        }else{
            return null;
        }
    }




    private Map<String, List<NodeInfo<T>>> groupByParentId(List<T> elements) throws Exception{
        Map<String, List<NodeInfo<T>>> result = new HashMap<>((int)(elements.size() * 1.34));
        for( T el : elements ){
            String parent = TreeHandler.defaultIfBlank(el.theParentId(), root.getId());
            if( parent.equals(el.theId()) ){
                throw new Exception("元素id和parentId不能相等[id='" + el.theId() + "']");
            }
            List<NodeInfo<T>> group = result.get(parent);
            if ( group == null ){
                group = new LinkedList<>();
                result.put(parent, group);
            }
            NodeInfo<T> node = this.creatNode(el);
            group.add(node);
            nodeMap.put(el.theId(), node);
        }
        return result;
    }




    public static boolean isBlank (String string) {
        return string == null || "".equals(string.trim());
    }

    public static String trimToEmpty (String string) {
        return isBlank(string) ? "" : string.trim();
    }
    private static String defaultIfBlank (String string, String defaultValue) {
        return isBlank(string) ? defaultValue : string;
    }
    public static boolean isEmpty (List<?> list) {
        return list == null || list.isEmpty();
    }
    public static boolean isNotEmpty (List<?> list) {
        return !isEmpty(list);
    }
}


喜闻乐见的呆毛Demo


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

/**
 * 任意一个对象,实现Nodeable接口
 * 返回id,和父节点id
 */
public class Demo implements Nodeable {

    private String id;
    private String pid;
	private String name;
	
    public Demo(String id, String pid){
        this.id = id;
        this.pid = pid;
    }
    
    @Override
    public String theId() {
        return id;
    }
    @Override
    public String theParentId() {
        return pid;
    }
   
   
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public String getPid() {
        return pid;
    }
    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    } 
    
    /**
    useroot
        10
            120
                4876
                    41344
                4582
                    45664
                    58761
            125
                451
                    454678
                        64487
                            510345
            115
        11
            153
            186
            178
        13
            137
                15130
                    5131
            158
                1783
                1568
            136


*/


    public static void main(String[] args) throws Exception{
        List<Demo> list = new ArrayList();
        list.add(new Demo("10", ""));
        list.add(new Demo("120", "10"));
        list.add(new Demo("4876", "120"));
        list.add(new Demo("41344", "4876"));
        list.add(new Demo("4582", "120"));
        list.add(new Demo("45664", "4582"));
        list.add(new Demo("58761", "4582"));
        list.add(new Demo("125", "10"));
        list.add(new Demo("451", "125"));
        list.add(new Demo("454678", "451"));
        list.add(new Demo("64487", "454678"));
        list.add(new Demo("510345", "64487"));
        list.add(new Demo("115", "10"));
        list.add(new Demo("11", ""));
        list.add(new Demo("153", "11"));
        list.add(new Demo("186", "11"));
        list.add(new Demo("178", "11"));
        list.add(new Demo("13", ""));
        list.add(new Demo("137", "13"));
        list.add(new Demo("15130", "137"));
        list.add(new Demo("5131", "15130"));
        list.add(new Demo("158", "13"));
        list.add(new Demo("1783", "158"));
        list.add(new Demo("1568", "158"));
        list.add(new Demo("136", "13"));

        TreeHandler<Demo> tree = new TreeHandler<>("useroot");
        NodeInfo<Demo> root = tree.parseArray(list);

        NodeInfo<Demo> childTree = tree.queryTree("4582");
        System.out.println(childTree.getParentIds());

        List<NodeInfo<Demo>> nodes = tree.getNodes();
        
        TreeHandler handler = tree.splitById("13", "");
        handler.getNodeMap();
        
    }
}

就酱~

乾杯 []( ̄▽ ̄)*




2018-11-10 plus版,优化递归遍历;增加分割子树




  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值