首页 > 技术文章 > Java生成菜单树(目录树)的几种方式

why5125 2019-09-09 17:28 原文

本文介绍两种不同生成多级目录树的方式:1. 递归生成,2.  map+list 集合生成。最下方会附上完整代码。

生成树的基本规则:子节点的par_id等于父节点的id。

1. 实体类

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);
    }
  }
}
View Code

注意使用build()插入数据时setChildren注意要判空处理,不然添加子节点时出现空引用

public void setChildren(ArrayList<Menu> children) {
    if (children==null){//防止添加子节点出现空引用
      children=new ArrayList<>();
    }
    this.children = children;
  }


2. 创建测试数据

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

生成如下数据:

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=[]}
View Code

3. 生成树(集合方式)

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;
    }
View Code

打印json如下:打印json时可以通过@JSONField(ordinal=1) 在实体类属性设置顺序

{
  "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": [
                        
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
View Code

4. 生成树(递归方式)

待续。。。。。

/*********************************************************************************************************************/

最后完整代码如下:

实体类:

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);
    }
  }
}
View Code

树生成类:

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

}
View Code

 

推荐阅读