java 树迭代-反向迭代

原创性声明:本文完全为笔者原创,请尊重笔者劳动力。转载务必注明原文地址。

上个礼拜碰到一个树结构生成的问题。迭代的的逻辑的确相对比较绕。特地write down下来。

【业务背景】:

有一批组对象,有表对象。表在组内,组可能嵌套组,但是组与表不能同时存在于一个组下。

【数据】:

1. List<ProductGroupModel> tableList; // 表对应的List
2. List<ProductGroupModel> groupList; //组对应的List

ps:ProductGroupModel 是对象模型,table和group对象都要转化为这个模型才方便构造树结构。

树结构如下:

Paste_Image.png

要把右边的数据转化为左边图示的结构,通过nodes属性,其数据类型为List。

只是此次的树存在一个特别的需求:就是要过滤空组,即组下没有表的组将不会出现在树里,比如上面的B,就要剔除。

思路分析:如何剔除其中的空组是比较棘手的问题,也就意味着要从表开始反向迭代,查找组,这样,没有表的组就不会被查到。如果在这个过程中同时生成树结构,操作会比较复杂容易出错。因此采取的步骤是:

1.首先根据表得到表的直接父组列表List<ProductGroupModel> parentList(也就是[D,E])。

/**
 * 传入表对应的tableList,返回这些表的直接上级组对应的parentList。
 * @param tableModelList, 即tableList
 * @param allProductGroupModel, 即groupList对应的哈希Map形式结构
 */
    private List<ProductGroupModel> getParentsOfTable(List<ProductGroupModel> tableModelList, 
                    Map<Long, ProductGroupModel> allProductGroupMap) {
        List<ProductGroupModel> parentsModelOfTableList = new ArrayList<ProductGroupModel>();
        Long parentId;
        for (ProductGroupModel pgmTable : tableModelList) {
            parentId = pgmTable.getParentId();
            ProductGroupModel parentModel = allProductGroupMap.get(parentId);
            if (parentModel != null) {
                parentsModelOfTableList.add(parentModel);
                allProductGroupMap.remove(parentId); //避免重复
            }
        }
        return parentsModelOfTableList;
    }

因此,在调用上面方法之前,还需要将groupList转化为HashMap形式,如下:

Map<Long, ProductGroupModel> allProductGroupModel = new HashMap<Long, ProductGroupModel>(); //存放所有的ProductGroupModel

//将所有的组ProductGroupModel放到Map中,后面将从中过滤出底下有table的
for (ProductGroupModel groupModel : groupList) { 
    allProductGroupModel.put(groupModel.getId(), groupModel);
}

再将allProductGroupModel和tableList传入1中的方法。此时getParentOfTable()就将返回parentList ([D,E])。

2.再根据parentList过滤groupList中的空组,返回非空组列表。直接上代码:

/**
 * 过滤所有的空组group,将所有存在table的group挑出来并返回,摈弃所有底下没有table的group(无论是直接还是间接下面)。
 * @param parentModelList 要显示的table对应的List<ProductGroupModel>
 * @param resultList 返回过滤出来的List<group>
 * @param resultMap  已经过滤出来的HashMap<Long, ProductGroupModel>
 * @param allProductGroupModelMap 所有的group节点
* @return 返回resultList
 */
private List<ProductGroupModel> filterParentGroupModel(List<ProductGroupModel> parentModelList,
            ArrayList<ProductGroupModel> resultList, HashMap<Long, ProductGroupModel> resultMap,
            Map<Long, ProductGroupModel> allProductGroupModelMap) {
        if (parentModelList == null || parentModelList.size() == 0) { // 当没有父节点时,就返回结果集
            return resultList;
        }
        
        // 重新创建父节点集合
        List<ProductGroupModel> newParentModelList = new ArrayList<ProductGroupModel>();
        // 遍历parentModelList
        for (ProductGroupModel pgm : parentModelList) {
            Long id = pgm.getId();
            Long parent_id = pgm.getParentId();
            
            //已经过滤出来的group节点不存在,则添加
            if (!resultMap.containsKey(id)) { //只处理组
                newParentModelList.add(pgm); //添加到父节点
                resultMap.put(id, pgm); //添加到已被过滤的map中
                allProductGroupModelMap.remove(id); // 溢出总集合中的元素
                resultList.add(pgm);
            }
            
            // 找出本节点的父节点并添加到newParentModelList父节点集合中,并移除集合中相应的元素
            if (parent_id != null && !"".equals(parent_id)) {
                ProductGroupModel parentModel = allProductGroupModelMap.get(parent_id);
                if (parentModel != null) {
                    newParentModelList.add(parentModel);
                    allProductGroupModelMap.remove(parent_id);
                }
            }
        }
        //递归调用
        filterParentGroupModel(newParentModelList, resultList, resultMap, allProductGroupModelMap);
        
        return resultList;
    }

一系列反向递归后,返回的resultList就是排除空组后的groupList,即hasTableGroupList

3.最后,再由上至下,正向迭代生成树。

/**
  * 传入表对应的List<ProductGroupModel>和组对应的List<ProductGroupModel>,将其转化为树结构,并返回。其中组已经经过处理,不含有空组。
  * @param groupModelList 组对应的List<ProductGroupModel>
  * @param tableModelList 表对应的List<ProductGroupModel>
  * @return List<ProductGroupModel>树结构,其中应当只有一个根节点
  */
    private List<ProductGroupModel> buildUpToTree(List<ProductGroupModel> groupModelList, List<ProductGroupModel> tableModelList, Long tableId) {
        groupModelList.addAll(tableModelList); //将表和组放到一起
        List<ProductGroupModel> rootNodes = new ArrayList<ProductGroupModel>(); // 存放当前allProductGroupModelList中的根节点
        List<ProductGroupModel> notRootNodes = new ArrayList<ProductGroupModel>(); //存放非根节点
        // 找出根节点
        if (groupModelList != null && groupModelList.size() > 0) {
            for (ProductGroupModel pgm : groupModelList) {
                if (pgm == null) continue;
                if (!pgm.getIstable() && pgm.getId() == Long.valueOf(pgm.getCpId())) { // 判断是否为根节点
                    rootNodes.add(pgm);
                } else {
                    notRootNodes.add(pgm);
                }
            }
        }
        //递归获取所有子节点
        if (rootNodes.size() > 0) {
            for (ProductGroupModel pgm : rootNodes) { // 遍历根节点。size应该要为1
                pgm.setIstable(false);
                pgm.setLevel(0);
                pgm.setNodes(getChildTreeData(notRootNodes, pgm.getId(), 0, tableId));
            }
        }
        return rootNodes;
    }

上面的方法是获取了根节点,是迭代的入口,迭代的函数如下:

/**
     * 迭代生成树方法。传入一个List和id,从List中找到id对应组的直接下层,并迭代调用
     * @param childList
     * @param id
     * @return
     */
    private List<ProductGroupModel> getChildTreeData(List<ProductGroupModel> childList, Long id, int level, Long tableId) {
        List<ProductGroupModel> parentNodes = new ArrayList<ProductGroupModel>();
        List<ProductGroupModel> childNodes = new ArrayList<ProductGroupModel>();
        if (childList != null && childList.size() > 0) { //找出当前的根节点和非根节点
            for (ProductGroupModel pgm : childList) {
                // 找出当前childList中的根节点
                if (pgm.getParentId().toString().equals(id.toString())) {
                    parentNodes.add(pgm);
                } else {
                    childNodes.add(pgm);
                }
            }
        }
        
        // 给parentNodes赋予子节点
        if (parentNodes.size() > 0) {
            //排序
            parentNodes.sort(modelComparator);
            int levelTemp = ++level;
            for (ProductGroupModel pgm : parentNodes) {
                List<ProductGroupModel> nodes;
                pgm.setLevel(levelTemp);
                // 定位table
                if (pgm.getIstable()) { //如果是表
                    nodes = null;
                } else {
                    nodes = getChildTreeData(childNodes, pgm.getId(), levelTemp, tableId);
                    pgm.setIstable(false);
                }
                // 递归查询子节点
                pgm.setNodes(nodes);
                
            }
        }
        
        return parentNodes;
    }

调用buildUpToTree即可得到树结构。核心的方法就是filterParentGroupModel()buildUpToTree()getChildTreeData()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容