java树结构转换工具类


背景

实际开发中经常会遇到需要由List转换成带父子级结构的树状数据,甚至于需要带父子级结构的搜索结果。本文针对这一场景提供了一种通用的list转换树结构的思路,希望可以对大家有所帮助。如有更好的方案或者可优化点欢迎在下方评论区留言。

实现

废话不多说,直接上代码。

TreeUtils.java - 实现该功能的主类,提供了转换的具体实现和一个用于扩展的接口。

package com.zjtx.tech.utils;

import com.sun.istack.internal.NotNull;

import java.util.*;

/**
 * @author 泽济天下
 * @date 2020年08月24日16:55
 * @description:
 */
public class TreeUtils<V> {

    private List<V> selectList;
    private List<V> allDataList;
    private DataHelper<V> dataHelper;

    /**
     * 提供多参构造方法
     * @param selectList 选中的或者搜索出来的
     * @param allDataList
     * @param dataHelper
     */
    public TreeUtils(List<V> selectList, @NotNull List<V> allDataList, DataHelper<V> dataHelper){
        this.selectList = selectList;
        this.allDataList = allDataList;
        this.dataHelper = dataHelper;
    }

    public List<V> convertTree(){
        //如果选中或者查询结果是空 直接返回
        if(selectList == null || selectList.size() == 0){
            return selectList;
        }

        Map<String, V> allDataMap = new LinkedHashMap<>();
        //list转存map 这里的list是所有数据的list
        for(V v: allDataList){
            String key = dataHelper.getKey(v);
            if(null == key){
                System.out.println("检测到存在key为空的数据,程序即将退出....");
                break;
            }
            allDataMap.put(dataHelper.getKey(v), v);
        }

        //转换前执行的操作 如果有需要的话
        if(dataHelper != null){
            dataHelper.before(allDataList, allDataMap);
        }

        Map<String, V> retMap = new LinkedHashMap<>();
        for (V item : selectList) {
            updateItemInfo(allDataMap, item, retMap);
        }

        List<V> result = new ArrayList<>(retMap.values());
        if(dataHelper != null){
            dataHelper.after(result);
        }

        return result;
    }

    /**
     * 更新节点信息
     * @param allDataMap
     * @param item
     * @param retMap
     */
    private void updateItemInfo(Map<String,V> allDataMap, V item, Map<String,V> retMap) {
        V v = allDataMap.get(dataHelper.getKey(item));
        if(v == null){
            System.out.println("allDataMap中获取指定key的数据返回空: " + dataHelper.getKey(item));
            return;
        }

        //判断是不是顶级节点
        String pid = dataHelper.getParentKey(item);
        if (pid == null || "0".equals(pid)) {
            //是顶级节点直接放到结果map里
            retMap.put(dataHelper.getKey(item), v);
            return;
        }

        //不是顶级节点-找到它的父
        V parent = allDataMap.get(pid);
        if(parent == null){
            System.out.println("allDataMap中获取指定key-pid的数据返回空: " + pid);
            System.out.println("异常数据信息:" + v);
            return;
        }

        //找到它的所有子
        List<V> children = dataHelper.getChildren(parent);
        if (children == null) {
            children = new LinkedList<>();
        }

        //替换指定元素
        if(children.size() > 0){
            int index = -1;
            for (int i = 0; i < children.size(); i++) {
                if(dataHelper.getKey(children.get(i)).equals(dataHelper.getKey(item))){
                    index = i;
                    break;
                }
            }
            if(index != -1){
                children.remove(index);
                children.add(index, v);
            }else {
                children.add(v);
            }
        } else {
            children.add(v);
        }
        dataHelper.setChildren(children, parent);
        allDataMap.put(pid, parent);
        updateItemInfo(allDataMap, parent, retMap);
    }


    public interface DataHelper<V> {

        /**
         * 转换之前的数据处理
         * @param srcData
         * @param srcMap
         */
        void before(List<V> srcData, Map<String, V> srcMap);

        /**
         * 转换之后的数据处理
         * @param resultList
         */
        void after(Collection<V> resultList);

        /**
         * 获取主键
         * @param item
         * @return
         */
        String getKey(V item);

        /**
         * 获取父键
         * @param item
         * @return
         */
        String getParentKey(V item);

        /**
         * 获取所有的子
         * @param item
         * @return
         */
        List<V> getChildren(V item);

        /**
         * 重新设置子
         * @param children
         * @param item
         */
        void setChildren(List<V> children, V item);
    }
}
测试

下面我们写个测试数据测试下,以菜单为例。

测试的main方法类

package com.zjtx.tech.utils;

import com.alibaba.fastjson.JSON;

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

/**
 * @author 泽济天下
 * @title TreeUtilsTest
 * @description list转换树工具测试类
 * @date 2020/8/24 0024 下午 21:24
 */
public class TreeUtilsTest {

    public static void main(String[] args) {
        //正常场景中 list都是查询出来的
        //这里的参数有两个list 一个是所有的数据allDataList 还有个选中的selectList
        //如果两个list一样就是返回完整的树结构 如果两个不一样会返回selectList对应的所有父子级结构
        //这个测试类只演示了返回完整树结构的操作 其他情况大家可以自己尝试下
        List<Menu> list = initMenuList();
        TreeUtils<Menu> treeUtils = new TreeUtils<>(list, list, new MenuDataHelper());
        List<Menu> menus = treeUtils.convertTree();
        String o = JSON.toJSON(menus).toString();
        System.out.println(o);
    }

    private static List<Menu> initMenuList() {

        List<Menu> list = new ArrayList<>();
        Menu menu1 = new Menu();
        menu1.setId("1");
        menu1.setParentId("0");
        menu1.setName("菜单1");
        list.add(menu1);

        Menu menu2 = new Menu();
        menu2.setId("2");
        menu2.setParentId("0");
        menu2.setName("菜单2");
        list.add(menu2);

        Menu menu3 = new Menu();
        menu3.setId("3");
        menu3.setParentId("1");
        menu3.setName("菜单11");
        list.add(menu3);

        Menu menu4 = new Menu();
        menu4.setId("4");
        menu4.setParentId("3");
        menu4.setName("菜单111");
        list.add(menu4);

        return list;
    }
}

涉及到的实体类

package com.zjtx.tech.utils;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 泽济天下
 * @title Menus
 * @description TODO
 * @date 2020/8/24 0024 下午 21:31
 */
public class Menu {

    private String id;

    private String name;

    private String parentId;

    private List<Menu> subMenus;

    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 List<Menu> getSubMenus() {
        return subMenus;
    }

    public void setSubMenus(List<Menu> subMenus) {
        this.subMenus = subMenus;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

还有个DataHelper的实现类,也比较简单,主要做了属性的转换。

package com.zjtx.tech.utils;

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

/**
 * @author 泽济天下
 * @title MenuDataHelper
 * @description TODO
 * @date 2020/8/24 0024 下午 21:37
 */
public class MenuDataHelper implements TreeUtils.DataHelper<Menu> {

    @Override
    public void before(List<Menu> srcData, Map<String, Menu> srcMap) {
    }

    @Override
    public void after(Collection<Menu> resultList) {
      
    }

    @Override
    public String getKey(Menu item) {
        return item.getId();
    }

    @Override
    public String getParentKey(Menu item) {
        return item.getParentId();
    }

    @Override
    public List<Menu> getChildren(Menu item) {
        return item.getSubMenus();
    }

    @Override
    public void setChildren(List<Menu> children, Menu item) {
        item.setSubMenus(children);
    }
}

运行结果:

[{"subMenus":[{"subMenus":[{"name":"菜单111","id":"4","parentId":"3"}],"name":"菜单11","id":"3","parentId":"1"}],"name":"菜单1","id":"1","parentId":"0"},{"name":"菜单2","id":"2","parentId":"0"}]

树结构转换工具测试结果图

以上就是所有内容,如果有更好的建议的话欢迎评论区留言。
如果有其他需求也可以留言反馈,共同讨论实现方案。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泽济天下

你的鼓励是我最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值