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())); } } } }