Java生成菜单树(目录树)的几种方式
本文介绍两种不同生成多级目录树的方式:1. 递归生成,2. map+list 集合生成。最下方会附上完整代码。
生成树的基本规则:子节点的par_id等于父节点的id。
1. 实体类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import java.util.ArrayList; public class Menu { private int id; private String menuName;//名称 private int parId;//上级ID private int type;//0:目录;1:菜单 private String url; public ArrayList<Menu> children; public Menu() { super(); this.children = new ArrayList<>(); } private Menu(Builder builder) { setId(builder.id); setMenuName(builder.menuName); setParId(builder.parId); setType(builder.type); setUrl(builder.url); setChildren(builder.children); } public void addChildren(Menu menu) { this.children.add(menu); } public ArrayList<Menu> getChildren() { return children; } public void setChildren(ArrayList<Menu> children) { if (children==null){//防止添加子节点出现空引用 children=new ArrayList<>(); } this.children = children; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMenuName() { return menuName; } public void setMenuName(String menuName) { this.menuName = menuName; } public int getParId() { return parId; } public void setParId(int parId) { this.parId = parId; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public String toString() { return "Menu{" + "id=" + id + ", menuName='" + menuName + '\'' + ", parId=" + parId + ", type=" + type + ", url='" + url + '\'' + ", children=" + children + '}'; } public static final class Builder { private int id; private String menuName; private int parId; private int type; private String url; private ArrayList<Menu> children; public Builder() { } public Builder id(int id) { this.id = id; return this; } public Builder menuName(String menuName) { this.menuName = menuName; return this; } public Builder parId(int parId) { this.parId = parId; return this; } public Builder type(int type) { this.type = type; return this; } public Builder url(String url) { this.url = url; return this; } public Builder children(ArrayList<Menu> children) { this.children = children; return this; } public Menu build() { return new Menu(this); } } }
注意使用build()插入数据时setChildren注意要判空处理,不然添加子节点时出现空引用
public void setChildren(ArrayList<Menu> children) {
if (children==null){//防止添加子节点出现空引用
children=new ArrayList<>();
}
this.children = children;
}
2. 创建测试数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static void main(String[] args) { createData(); dataList.forEach(e-> System.out.println(e.toString())); } //保存数据 public static List<Menu> dataList=new ArrayList(); //创建数据 public static void createData(){ dataList.add(new Menu.Builder().id(0).menuName("根节点").parId(-2).type(0).url("").build()); for (int i = 1; i <50 ; i++) { if (i<11) { dataList.add(new Menu.Builder().id(i).menuName("1级节点"+i).parId(0).type(0).url("").build()); }else { dataList.add(new Menu.Builder().id(i).menuName(((i/10)+1)+"级节点"+i).parId(i-10).type(0).url("").build()); } } }
生成如下数据:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Menu{id=0, menuName='根节点', parId=-2, type=0, url='', children=[]} Menu{id=1, menuName='1级节点1', parId=0, type=0, url='', children=[]} Menu{id=2, menuName='1级节点2', parId=0, type=0, url='', children=[]} Menu{id=3, menuName='1级节点3', parId=0, type=0, url='', children=[]} Menu{id=4, menuName='1级节点4', parId=0, type=0, url='', children=[]} Menu{id=5, menuName='1级节点5', parId=0, type=0, url='', children=[]} Menu{id=6, menuName='1级节点6', parId=0, type=0, url='', children=[]} Menu{id=7, menuName='1级节点7', parId=0, type=0, url='', children=[]} Menu{id=8, menuName='1级节点8', parId=0, type=0, url='', children=[]} Menu{id=9, menuName='1级节点9', parId=0, type=0, url='', children=[]} Menu{id=10, menuName='1级节点10', parId=0, type=0, url='', children=[]} Menu{id=11, menuName='2级节点11', parId=1, type=0, url='', children=[]} Menu{id=12, menuName='2级节点12', parId=2, type=0, url='', children=[]} Menu{id=13, menuName='2级节点13', parId=3, type=0, url='', children=[]} Menu{id=14, menuName='2级节点14', parId=4, type=0, url='', children=[]} Menu{id=15, menuName='2级节点15', parId=5, type=0, url='', children=[]} Menu{id=16, menuName='2级节点16', parId=6, type=0, url='', children=[]} Menu{id=17, menuName='2级节点17', parId=7, type=0, url='', children=[]} Menu{id=18, menuName='2级节点18', parId=8, type=0, url='', children=[]} Menu{id=19, menuName='2级节点19', parId=9, type=0, url='', children=[]} Menu{id=20, menuName='3级节点20', parId=10, type=0, url='', children=[]} Menu{id=21, menuName='3级节点21', parId=11, type=0, url='', children=[]} Menu{id=22, menuName='3级节点22', parId=12, type=0, url='', children=[]} Menu{id=23, menuName='3级节点23', parId=13, type=0, url='', children=[]} Menu{id=24, menuName='3级节点24', parId=14, type=0, url='', children=[]} Menu{id=25, menuName='3级节点25', parId=15, type=0, url='', children=[]} Menu{id=26, menuName='3级节点26', parId=16, type=0, url='', children=[]} Menu{id=27, menuName='3级节点27', parId=17, type=0, url='', children=[]} Menu{id=28, menuName='3级节点28', parId=18, type=0, url='', children=[]} Menu{id=29, menuName='3级节点29', parId=19, type=0, url='', children=[]} Menu{id=30, menuName='4级节点30', parId=20, type=0, url='', children=[]} Menu{id=31, menuName='4级节点31', parId=21, type=0, url='', children=[]} Menu{id=32, menuName='4级节点32', parId=22, type=0, url='', children=[]} Menu{id=33, menuName='4级节点33', parId=23, type=0, url='', children=[]} Menu{id=34, menuName='4级节点34', parId=24, type=0, url='', children=[]} Menu{id=35, menuName='4级节点35', parId=25, type=0, url='', children=[]} Menu{id=36, menuName='4级节点36', parId=26, type=0, url='', children=[]} Menu{id=37, menuName='4级节点37', parId=27, type=0, url='', children=[]} Menu{id=38, menuName='4级节点38', parId=28, type=0, url='', children=[]} Menu{id=39, menuName='4级节点39', parId=29, type=0, url='', children=[]} Menu{id=40, menuName='5级节点40', parId=30, type=0, url='', children=[]} Menu{id=41, menuName='5级节点41', parId=31, type=0, url='', children=[]} Menu{id=42, menuName='5级节点42', parId=32, type=0, url='', children=[]} Menu{id=43, menuName='5级节点43', parId=33, type=0, url='', children=[]} Menu{id=44, menuName='5级节点44', parId=34, type=0, url='', children=[]} Menu{id=45, menuName='5级节点45', parId=35, type=0, url='', children=[]} Menu{id=46, menuName='5级节点46', parId=36, type=0, url='', children=[]} Menu{id=47, menuName='5级节点47', parId=37, type=0, url='', children=[]} Menu{id=48, menuName='5级节点48', parId=38, type=0, url='', children=[]} Menu{id=49, menuName='5级节点49', parId=39, type=0, url='', children=[]}
3. 生成树(集合方式)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static Map<String, Object> getTreeByColl(List<Menu> list) { Map<String, Object> resmap=new HashMap<>(); Map<Integer, Menu> map; List<Menu> treelist= new ArrayList<>(); if (null==list||list.isEmpty()){ return null; } map = list.stream().collect(Collectors.toMap(Menu::getId, a -> a,(k1, k2)->k1)); // 将list集合对象转换为json的字符串 // 如果id是父级的话就放入tree中treelist for (Menu menu : list) { if (null==map.get(menu.getParId())) { treelist.add(menu); } else { // 子级通过父id获取到父级的类型 Menu parent = map.get(menu.getParId()); // 父级获得子级,再将子级放到对应的父级中 parent.addChildren(menu); } } resmap.put("data",treelist); return resmap; }
打印json如下:打印json时可以通过@JSONField(ordinal=1) 在实体类属性设置顺序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ "data": [ { "id": 0, "menuName": "根节点", "parId": -2, "type": 0, "url": "", "children": [ { "id": 1, "menuName": "1级节点1", "parId": 0, "type": 0, "url": "", "children": [ { "id": 11, "menuName": "2级节点11", "parId": 1, "type": 0, "url": "", "children": [ { "id": 21, "menuName": "3级节点21", "parId": 11, "type": 0, "url": "", "children": [ { "id": 31, "menuName": "4级节点31", "parId": 21, "type": 0, "url": "", "children": [ { "id": 41, "menuName": "5级节点41", "parId": 31, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 2, "menuName": "1级节点2", "parId": 0, "type": 0, "url": "", "children": [ { "id": 12, "menuName": "2级节点12", "parId": 2, "type": 0, "url": "", "children": [ { "id": 22, "menuName": "3级节点22", "parId": 12, "type": 0, "url": "", "children": [ { "id": 32, "menuName": "4级节点32", "parId": 22, "type": 0, "url": "", "children": [ { "id": 42, "menuName": "5级节点42", "parId": 32, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 3, "menuName": "1级节点3", "parId": 0, "type": 0, "url": "", "children": [ { "id": 13, "menuName": "2级节点13", "parId": 3, "type": 0, "url": "", "children": [ { "id": 23, "menuName": "3级节点23", "parId": 13, "type": 0, "url": "", "children": [ { "id": 33, "menuName": "4级节点33", "parId": 23, "type": 0, "url": "", "children": [ { "id": 43, "menuName": "5级节点43", "parId": 33, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 4, "menuName": "1级节点4", "parId": 0, "type": 0, "url": "", "children": [ { "id": 14, "menuName": "2级节点14", "parId": 4, "type": 0, "url": "", "children": [ { "id": 24, "menuName": "3级节点24", "parId": 14, "type": 0, "url": "", "children": [ { "id": 34, "menuName": "4级节点34", "parId": 24, "type": 0, "url": "", "children": [ { "id": 44, "menuName": "5级节点44", "parId": 34, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 5, "menuName": "1级节点5", "parId": 0, "type": 0, "url": "", "children": [ { "id": 15, "menuName": "2级节点15", "parId": 5, "type": 0, "url": "", "children": [ { "id": 25, "menuName": "3级节点25", "parId": 15, "type": 0, "url": "", "children": [ { "id": 35, "menuName": "4级节点35", "parId": 25, "type": 0, "url": "", "children": [ { "id": 45, "menuName": "5级节点45", "parId": 35, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 6, "menuName": "1级节点6", "parId": 0, "type": 0, "url": "", "children": [ { "id": 16, "menuName": "2级节点16", "parId": 6, "type": 0, "url": "", "children": [ { "id": 26, "menuName": "3级节点26", "parId": 16, "type": 0, "url": "", "children": [ { "id": 36, "menuName": "4级节点36", "parId": 26, "type": 0, "url": "", "children": [ { "id": 46, "menuName": "5级节点46", "parId": 36, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 7, "menuName": "1级节点7", "parId": 0, "type": 0, "url": "", "children": [ { "id": 17, "menuName": "2级节点17", "parId": 7, "type": 0, "url": "", "children": [ { "id": 27, "menuName": "3级节点27", "parId": 17, "type": 0, "url": "", "children": [ { "id": 37, "menuName": "4级节点37", "parId": 27, "type": 0, "url": "", "children": [ { "id": 47, "menuName": "5级节点47", "parId": 37, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 8, "menuName": "1级节点8", "parId": 0, "type": 0, "url": "", "children": [ { "id": 18, "menuName": "2级节点18", "parId": 8, "type": 0, "url": "", "children": [ { "id": 28, "menuName": "3级节点28", "parId": 18, "type": 0, "url": "", "children": [ { "id": 38, "menuName": "4级节点38", "parId": 28, "type": 0, "url": "", "children": [ { "id": 48, "menuName": "5级节点48", "parId": 38, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 9, "menuName": "1级节点9", "parId": 0, "type": 0, "url": "", "children": [ { "id": 19, "menuName": "2级节点19", "parId": 9, "type": 0, "url": "", "children": [ { "id": 29, "menuName": "3级节点29", "parId": 19, "type": 0, "url": "", "children": [ { "id": 39, "menuName": "4级节点39", "parId": 29, "type": 0, "url": "", "children": [ { "id": 49, "menuName": "5级节点49", "parId": 39, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] }, { "id": 10, "menuName": "1级节点10", "parId": 0, "type": 0, "url": "", "children": [ { "id": 20, "menuName": "3级节点20", "parId": 10, "type": 0, "url": "", "children": [ { "id": 30, "menuName": "4级节点30", "parId": 20, "type": 0, "url": "", "children": [ { "id": 40, "menuName": "5级节点40", "parId": 30, "type": 0, "url": "", "children": [ ] } ] } ] } ] } ] } ] }
4. 生成树(递归方式)
待续。。。。。
/*********************************************************************************************************************/
最后完整代码如下:
实体类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import com.alibaba.fastjson.annotation.JSONField; import java.util.ArrayList; public class Menu { @JSONField(ordinal=1) private int id; @JSONField(ordinal=2) private String menuName;//名称 @JSONField(ordinal=3) private int parId;//上级ID @JSONField(ordinal=4) private int type;//0:目录;1:菜单 @JSONField(ordinal=5) private String url; @JSONField(ordinal=6) public ArrayList<Menu> children; public Menu() { super(); this.children = new ArrayList<>(); } private Menu(Builder builder) { setId(builder.id); setMenuName(builder.menuName); setParId(builder.parId); setType(builder.type); setUrl(builder.url); setChildren(builder.children); } public void addChildren(Menu menu) { this.children.add(menu); } public ArrayList<Menu> getChildren() { return children; } public void setChildren(ArrayList<Menu> children) { if (children==null){ children=new ArrayList<>(); } this.children = children; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMenuName() { return menuName; } public void setMenuName(String menuName) { this.menuName = menuName; } public int getParId() { return parId; } public void setParId(int parId) { this.parId = parId; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public String toString() { return "Menu{" + "id=" + id + ", menuName='" + menuName + '\'' + ", parId=" + parId + ", type=" + type + ", url='" + url + '\'' + ", children=" + children + '}'; } public static final class Builder { private int id; private String menuName; private int parId; private int type; private String url; private ArrayList<Menu> children; public Builder() { } public Builder id(int id) { this.id = id; return this; } public Builder menuName(String menuName) { this.menuName = menuName; return this; } public Builder parId(int parId) { this.parId = parId; return this; } public Builder type(int type) { this.type = type; return this; } public Builder url(String url) { this.url = url; return this; } public Builder children(ArrayList<Menu> children) { this.children = children; return this; } public Menu build() { return new Menu(this); } } }
树生成类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import com.alibaba.fastjson.JSONObject; import java.util.*; import java.util.stream.Collectors; public class TreeUtil { public static void main(String[] args) { createData(); //dataList.forEach(e-> System.out.println(e.toString())); System.out.println(JSONObject.toJSONString(getTreeByColl(dataList))); } //保存数据 public static List<Menu> dataList=new ArrayList(); //创建数据 public static void createData(){ dataList.add(new Menu.Builder().id(0).menuName("根节点").parId(-2).type(0).url("").build()); for (int i = 1; i <50 ; i++) { if (i<11) { dataList.add(new Menu.Builder().id(i).menuName("1级节点"+i).parId(0).type(0).url("").build()); }else { dataList.add(new Menu.Builder().id(i).menuName(((i/10)+1)+"级节点"+i).parId(i-10).type(0).url("").build()); } } } //System.out.println(getTreeByColl(dataList)); /** * 通过集合方式生成树 * @param list * @return */ public static Map<String, Object> getTreeByColl(List<Menu> list) { Map<String, Object> resmap=new HashMap<>(); Map<Integer, Menu> map; List<Menu> treelist= new ArrayList<>(); if (null==list||list.isEmpty()){ return null; } map = list.stream().collect(Collectors.toMap(Menu::getId, a -> a,(k1, k2)->k1)); /*List<Map.Entry<Integer, Menu>> listMap = new ArrayList<Map.Entry<Integer, Menu>>(map.entrySet()); Collections.sort(listMap, new Comparator<Map.Entry<Integer, Menu>>() { public int compare(Map.Entry<Integer, Menu> o1, Map.Entry<Integer, Menu> o2) { return o2.getKey().compareTo(o1.getKey()); } });*/ // 将list集合对象转换为json的字符串 // 如果id是父级的话就放入tree中treelist for (Menu menu : list) { if (null==map.get(menu.getParId())) { treelist.add(menu); } else { // 子级通过父id获取到父级的类型 Menu parent = map.get(menu.getParId()); // 父级获得子级,再将子级放到对应的父级中 parent.addChildren(menu); } } resmap.put("data",treelist); return resmap; } }
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如有问题,可以通过邮件985843242@qq.com联系。欢迎共同交流讨论。