package com.company.tree;
import java.util.ArrayList;
import java.util.List;
/**
* 基础树属性字段
* todo 可以在此基础上扩展基础字段
*
* @author ldb
* @ClassName BaseTree.java
* @Data 2022-02-25 11:00
*/
public class BaseTree<T> {
/**
* id
*/
String id;
/**
* 上级id
*/
String ParentId;
/**
* 菜单等级
*/
Integer level;
/**
* 子菜单
*/
List<T> Children;
/**
* 菜单名称
*/
String name;
/**
* 排序编号
*/
Integer orderId;
/**
* id链
*/
List<String> ids = new ArrayList<>();
public String getId() {
return id;
}
public List<String> getIds() {
return ids;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return ParentId;
}
public void setParentId(String parentId) {
ParentId = parentId;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public List<T> getChildren() {
return Children;
}
public void setChildren(List<T> children) {
Children = children;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
}
package com.company.tree;
import java.io.Serializable;
/**
* 菜单
* todo 这个类是给用户显示数据的类,可以扩展额外属性
*
* @author ldb
* @ClassName Menu.java
* @Data 2022-02-25 9:54
*/
public class Menu extends BaseTree implements Serializable {
public Menu(String id, String name, String parentId, Integer level, Integer orderId) {
this.id = id;
this.name = name;
this.ParentId = parentId;
this.level = level;
this.orderId = orderId;
}
}
package com.company.tree;
import com.alibaba.fastjson.JSON;
import java.util.*;
/**
* 树工具类
* 具体业务思路:
* 新增:如果有上级节点,则找到上级并将当前等级加一即可
* 修改: 主要修改的是上级节点和等级,同新增差不多
* 删除: 删除当前节点,其子节点也会被删除(可以先查询该节点有没有下级id链)
* 查询: 查询id链(这里可以通过递归查询)
* 需要依赖:fastjson.jar
*
* @author ldb
* @ClassName TreeTest.java
* @Data 2022-02-25 9:57
*/
public class TreeUtil {
public static List<Menu> menuTree = new ArrayList<>();
static {
menuTree.add(new Menu("1", "用户管理", "", 0, 0));
menuTree.add(new Menu("2", "用户新增", "1", 1, 0));
menuTree.add(new Menu("3", "用户修改", "1", 1, 0));
menuTree.add(new Menu("4", "用户删除", "1", 1, 0));
menuTree.add(new Menu("5", "用户删除-批量删除", "4", 2, 0));
menuTree.add(new Menu("8", "用户删除-批量删除-全部删除", "5", 3, 0));
menuTree.add(new Menu("6", "菜单管理", "", 0, 5));
menuTree.add(new Menu("7", "系统管理", "", 0, 8));
}
public static void main(String[] args) {
List<Menu> res = buildTree(menuTree);
System.out.println("获取树形列表(通过集合获取):");
System.out.println(JSON.toJSONString(res));
System.out.println("在集合中查找id链(通过集合获取):");
List<String> ids = findNodeIdsById(menuTree, "8");
System.out.println(JSON.toJSONString(ids));
System.out.println("在树中查找指定节点(通过树获取):");
Menu node = findNodeById(res, "8");
System.out.println(JSON.toJSONString(node));
}
/**
* 构建树
*
* @param menuTree 集合列表
* @return java.util.List<com.company.tree.Menu>
* @author Ldb
* @date 2022-02-25
**/
public static <T extends BaseTree> List<T> buildTree(List<T> menuTree) {
// 初始化id链(这里的id链其实是可以提前就确定的)
for (T t : menuTree) {
t.setIds(findNodeIdsById(menuTree, t.getId()));
}
List<T> res = new ArrayList<>();
Map<String, T> menuMap = new HashMap<>(5);
// 集合转map
for (T menu : menuTree) {
menuMap.put(menu.getId(), menu);
}
// 再将一级菜单下的子菜单找到并放入到集合中
for (Map.Entry<String, T> node : menuMap.entrySet()) {
for (T menu : menuTree) {
String parentId = menu.getParentId();
if (!isEmpty(parentId) && parentId.equals(node.getKey())) {
T firth = node.getValue();
// 为空验证
if (null == firth.getChildren()) {
firth.setChildren(new ArrayList<>());
}
firth.getChildren().add(menu);
menuMap.put(firth.getId(), firth);
}
}
}
// 最后获取所有一级菜单
for (Map.Entry<String, T> node : menuMap.entrySet()) {
if (node.getValue().getLevel().equals(0)) {
res.add(node.getValue());
}
}
return res;
}
/**
* 查询树中某个节点(从顶级依次递归查找)
*
* @param menuTree 树型数据
* @param id 要查找的id
* @author Ldb
* @date 2022-02-26
**/
public static <T extends BaseTree> T findNodeById(List<T> menuTree, String id) {
for (T curNode : menuTree) {
// 从第一层开始查找,如果是就直接返回该对象
if (curNode.getId().equals(id)) {
return curNode;
}
// 如果第一层没有找到,则看下面有没有子节点,如果有子节点则递归查找
if (null != curNode.getChildren() && curNode.getChildren().size() > 0) {
return (T) findNodeById(curNode.getChildren(), id);
}
}
// 如果没有找到任何节点,则默认返回空(当前层可能出现并没有找到得情况,所以上面也要return)
return null;
}
/**
* 查询某一个节点的id链
*
* @param list 集合列表
* @param id 节点id
* @return java.util.List<java.lang.String>
* @author Ldb
* @date 2022-02-26
**/
public static <T extends BaseTree> List<String> findNodeIdsById(List<T> list, String id) {
Map<String, T> treeMaps = new HashMap<>(5);
// 将所有列表添加到Map集合中
for (T t : list) {
treeMaps.put(t.getId(), t);
}
// 获取ids链
List<String> ids = findIds(treeMaps, id, new ArrayList<>());
if (ids == null) {
return null;
}
// 因为id链是从尾部开始查找的,所以这里需要进行反转
Collections.reverse(ids);
return ids;
}
/**
* 递归查找id链
*
* @param treeMaps 菜单列表Map
* @param id 要找的节点
* @param ids id链
* @return java.util.List<java.lang.String>
* @author Ldb
* @date 2022-02-26
**/
private static <T extends BaseTree> List<String> findIds(Map<String, T> treeMaps, String id, List<String> ids) {
// 将当前id添加到集合中
T t = treeMaps.get(id);
if (t == null) {
return null;
}
ids.add(t.getId());
if (!isEmpty(t.getParentId())) {
// 如果当前id有上级id则继续添加
return findIds(treeMaps, t.getParentId(), ids);
}
// 获取结束后返回
return ids;
}
/**
* 判断字符串是否为空
*
* @param str 字符串
* @return boolean
* @author Ldb
* @date 2022-02-26
**/
public static boolean isEmpty(String str) {
if (null == str || str.trim().equals("")) {
return true;
}
return false;
}
}