java实现树形结构数据

树形结构很常见,最典型就是管理系统中的菜单,比如用户管理下,有新增用户,删除用户等等.

数据库表中的存储结构 一般会有一个id 以及一些业务字段 最后一定要有一个父id 存储上一级的id

这样就建立了一个级联关系,我这里就没有去查询数据库了,直接创建几个记录即可.

菜单实体类

public class Menu {

    private Integer id;

    private String name;

    private String url;

    private Integer fatherId;


    public Menu(Integer id, String name, String url, Integer fatherId) {
        this.id = id;
        this.name = name;
        this.url = url;
        this.fatherId = fatherId;
    }

    @Override
    public String toString() {
        return "Menu{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", url='" + url + '\'' +
                ", fatherId=" + fatherId +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Integer getFatherId() {
        return fatherId;
    }

    public void setFatherId(Integer fatherId) {
        this.fatherId = fatherId;
    }
}

在新建一个类,和这个菜单实体类相同,但是多一个子节点属性,存放所有的子节点,其实这个不建,就在菜单实体类多加一个子节点属性也可以

public class MenuVo {
    private Integer id;

    private String name;

    private String url;

    private Integer fatherId;

    private List<MenuVo>  childNode;

    @Override
    public String toString() {
        return "MenuVo{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", url='" + url + '\'' +
                ", fatherId=" + fatherId +
                ", childNode=" + childNode +
                '}';
    }

    public MenuVo() {
    }

    public MenuVo(Integer id, String name, String url, Integer fatherId, List<MenuVo> childNode) {
        this.id = id;
        this.name = name;
        this.url = url;
        this.fatherId = fatherId;
        this.childNode = childNode;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Integer getFatherId() {
        return fatherId;
    }

    public void setFatherId(Integer fatherId) {
        this.fatherId = fatherId;
    }

    public List<MenuVo> getChildNode() {
        return childNode;
    }

    public void setChildNode(List<MenuVo> childNode) {
        this.childNode = childNode;
    }
}

接下来贴出测试代码

public class Test  {
    public static void main(String[] args) {

        List<Menu> sqlData=new ArrayList<>();
        sqlData.add(new Menu(1,"1","xxx",-1));
        sqlData.add(new Menu(2,"1-1","xxx",1));
        sqlData.add(new Menu(3,"1-1-1","xxx",2));
        sqlData.add(new Menu(4,"2","xxx",-1));
        sqlData.add(new Menu(5,"2-1","xxx",4));
        sqlData.add(new Menu(6,"1-2","xxx",1));

        //存储非根节点
        List<MenuVo> tempList=new ArrayList<>();
        //存储最终的结果
        List<MenuVo> resultList=new ArrayList<>();
        //遍历数据库查询数据集合,如果父id==-1 代表根节点 添加到最终结果中,否则为非根节点添加到临时节点
        for (Menu menu : sqlData) {
            if(menu.getFatherId()==-1){
                resultList.add(new MenuVo(menu.getId(),menu.getName(),menu.getUrl(),menu.getFatherId(),new ArrayList<>()));
            }else{
                tempList.add(new MenuVo(menu.getId(),menu.getName(),menu.getUrl(),menu.getFatherId(),new ArrayList<>()));
            }
        }
        //遍历所有的根节点,通过根节点和非根节点集合,找到这个根节点的所有子节点
        for (MenuVo menuVo : resultList) {
            getChildNode(tempList,menuVo);
        }

        resultList.forEach(System.out::println);
    }


    public static  void getChildNode(List<MenuVo> tempList,MenuVo fatherNode  ){
        for (MenuVo menuVo : tempList) {
            //如果该节点的父id为传进来父节点的id 那么就为其子节点
            if(menuVo.getFatherId()==fatherNode.getId()){
                //添加到子节点数组
                fatherNode.getChildNode().add(menuVo);
                //递归调用 继续找该子节点  是否还有子节点
                getChildNode(tempList,menuVo);
            }
        }
    }


}

大致思路就是,先区分根节点和非根节点,然后遍历根节点,根据根节点的id,去非根节点中找父id

为根节点id的节点,添加到根节点的子节点集合中,但是由于子节点还可能有子节点,就是这个树有

多少层级,我们并不知道,所以在查找子节点时需要递归,将每一个子节点作为一个父节点继续去查找他的子节点

 

想了想其实子节点递归这里可以优化,可以将非根节点,根据父id分为一组,就是Map<父id,List<MenuVo>>

以这个格式存储,这样在递归子节点时,不需要遍历去找子节点,直接根据id查找子节点即可

把代码贴出来

public class Test  {
    public static void main(String[] args) {

        List<Menu> sqlData=new ArrayList<>();
        sqlData.add(new Menu(1,"1","xxx",-1));
        sqlData.add(new Menu(2,"1-1","xxx",1));
        sqlData.add(new Menu(3,"1-1-1","xxx",2));
        sqlData.add(new Menu(4,"2","xxx",-1));
        sqlData.add(new Menu(5,"2-1","xxx",4));
        sqlData.add(new Menu(6,"1-2","xxx",1));

        //存储非根节点
        Map<Integer,List<MenuVo>> tempMap=new HashMap<>();

        //存储最终的结果
        List<MenuVo> resultList=new ArrayList<>();
        //遍历数据库查询数据集合,如果父id==-1 代表根节点 添加到最终结果中,否则为非根节点添加到临时节点
        for (Menu menu : sqlData) {
            MenuVo menuVo=new MenuVo(menu.getId(),menu.getName(),menu.getUrl(),menu.getFatherId(),new ArrayList<>());
            if(menu.getFatherId()==-1){
                resultList.add(menuVo);
            }else{
                if (tempMap.containsKey(menu.getFatherId())) {
                    tempMap.get(menu.getFatherId()).add(menuVo);
                }else{
                    List<MenuVo> list=new ArrayList<>();
                    list.add(menuVo);
                    tempMap.put(menu.getFatherId(),list);
                }
            }
        }
        //遍历所有的根节点,通过根节点和非根节点集合,找到这个根节点的所有子节点
        for (MenuVo menuVo : resultList) {
            getChildNode(tempMap,Arrays.asList(menuVo));
        }

        resultList.forEach(System.out::println);
    }


    public static  void getChildNode(Map<Integer,List<MenuVo>> tempMap,List<MenuVo> fatherNodeList  ){
        for (MenuVo menuVo : fatherNodeList) {
            if (tempMap.containsKey(menuVo.getId())) {
               menuVo.getChildNode().addAll(tempMap.get(menuVo.getId()));
               getChildNode(tempMap,tempMap.get(menuVo.getId()));
            }
        }
    }


}

 

posted @ 2020-04-19 15:59  沙漠里的橘子皮  阅读(2939)  评论(3编辑  收藏  举报