实体类
package org.example;
import lombok.Data;
import java.util.List;
@Data
public class Menu {
/**
* id
*/
private String id;
/**
* 菜单名称
*/
private String name;
/**
* 父节点id
*/
private String pid;
/**
* 子节点
*/
private List<Menu> children;
/**
* 带参构造
*/
public Menu(String id, String name, String pid) {
this.id = id;
this.name = name;
this.pid = pid;
}
}
1.遍历属性结构(多个根节点)
package org.example;
import com.alibaba.fastjson2.JSONObject;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
//模拟数据库查出所有数据(多个根节点)
List<Menu> menus = Arrays.asList(
new Menu("1", "根节点1", "0"),
new Menu("2", "子节点1-1", "1"),
new Menu("3", "子子节点1-1-1", "2"),
new Menu("4", "子子子节点1-1-1-1", "3"),
new Menu("5", "子节点1-2", "1"),
new Menu("6", "子子节点1-2-1", "5"),
new Menu("7", "根节点2", "0"),
new Menu("8", "子节点2-1", "7"),
new Menu("9", "子子节点2-1-1", "8")
);
List<Menu> tree = getTree(menus);
System.out.println(JSONObject.toJSONString(tree));
}
/**
* 遍历树(多个根节点)
*
* @param menus
*/
public static List<Menu> getTree(List<Menu> menus) {
// 筛选出根节点
return menus.stream().filter(m -> Objects.equals(m.getPid(), "0")).map(m ->
{
//设置每个树的子节点
m.setChildren(getChildrens(m, menus));
return m;
}).collect(Collectors.toList());
}
/**
* 获取子节点
*
* @param root
* @param menus
* @return
*/
public static List<Menu> getChildrens(Menu root, List<Menu> menus) {
//筛选直接子节点
return menus.stream().filter(m -> Objects.equals(m.getPid(), root.getId())).map(
m -> {
// 递归设置子节点
m.setChildren(getChildrens(m, menus));
return m;
}
).collect(Collectors.toList());
}
}
输出:
[{"children":[{"children":[{"children":[{"children":[],"id":"4","name":"子子子节点1-1-1-1","pid":"3"}],"id":"3","name":"子子节点1-1-1","pid":"2"}],"id":"2","name":"子节点1-1","pid":"1"},{"children":[{"children":[],"id":"6","name":"子子节点1-2-1","pid":"5"}],"id":"5","name":"子节点1-2","pid":"1"}],"id":"1","name":"根节点1","pid":"0"},{"children":[{"children":[{"children":[],"id":"9","name":"子子节点2-1-1","pid":"8"}],"id":"8","name":"子节点2-1","pid":"7"}],"id":"7","name":"根节点2","pid":"0"}]
2.获取节点路径path
一般新增节点时将节点路径存入数据库,便于查看从根节点到子节点的路径
/**
* 获取节点路径
*
* @param id 要计算的节点id
* @param map
* @return
*/
public static String getPath(String id, Map<String, String> map) {
if (map == null || map.isEmpty()) {
return null;
}
List<String> pathList = new ArrayList<>();
// 构建路径
buildPath(id, map, pathList);
// 翻转
Collections.reverse(pathList);
return StringUtils.join(pathList, ",");
}
/**
* 构建路径
*
* @param id 节点id
* @param map
* @param pathList 路径
*/
public static void buildPath(String id, Map<String, String> map, List<String> pathList) {
if (Objects.isNull(id) || Objects.equals(id, "0")) {
return;
}
pathList.add(id);
String pid = map.get(id);
buildPath(pid, map, pathList);
}
测试:
public static void main(String[] args) {
//模拟数据库查出所有数据(多个根节点)
List<Menu> menus = Arrays.asList(
new Menu("1", "根节点1", "0"),
new Menu("2", "子节点1-1", "1"),
new Menu("3", "子子节点1-1-1", "2"),
new Menu("4", "子子子节点1-1-1-1", "3"),
new Menu("5", "子节点1-2", "1"),
new Menu("6", "子子节点1-2-1", "5"),
new Menu("7", "根节点2", "0"),
new Menu("8", "子节点2-1", "7"),
new Menu("9", "子子节点2-1-1", "8")
);
// 模拟从数据库查询出父子关系
Map<String, String> map = menus.stream().collect(Collectors.toMap(Menu::getId,
Menu::getPid));
System.out.println(getPath("1", map)); // 1
System.out.println(getPath("3", map)); // 1,2,3
System.out.println(getPath("9", map)); // 7,8,9
}