不废话,直接上代码
一、工具函数
可以直接使用list2tree()实现列表转树形结构
package com.server.utils.tree;
import org.springframework.beans.BeanUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author visy.wang
* @date 2024/6/27 21:27
*/
public class TreeUtil {
public static <T,K,R> R list2tree(List<T> list,
K rootPid,
Function<T,K> idGetter,
Function<T,K> pidGetter,
Function<T,R> nodeGetter,
String childrenName,
Supplier<R> nodeSupplier,
BiConsumer<R,R> childAdder){
Map<K, R> map = new HashMap<>();
for (T t : list) {
K id = idGetter.apply(t), pid = pidGetter.apply(t);
//查找当前节点
R node = map.get(id);
if(node == null){//当前节点不存在则创建
node = nodeGetter.apply(t);
map.put(id, node);
}else{//当前节点已存在(被其他节点以父节点加入),补全剩余字段
BeanUtils.copyProperties(nodeGetter.apply(t), node, childrenName);
}
//查找父节点
R parent = map.get(pid);
if(parent == null){//父节点不存在,则创建父节点,并将自身添加到父节点的子节点集合中
parent = nodeSupplier.get();
childAdder.accept(parent, node);
map.put(pid, parent);
}else{//父节点已存在,直接将自身添加到父节点的子节点集合中
childAdder.accept(parent, node);
}
}
return map.get(rootPid);
}
}
二、原始对象
package com.server.utils.tree;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 菜单
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 菜单id
*/
private Long id;
/**
* 父id
*/
private Long fid;
/**
* 机构名称
*/
private String name;
/**
* 模块id
*/
private Integer level;
/**
* 状态 1 启用 2 停用
*/
private Integer status;
/**
* 权重
*/
private Integer weight;
}
三、节点对象
package com.server.utils.tree;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
/**
* @author visy.wang
* @date 2024/6/27 21:54
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class MenuNode extends Menu { //不一定要继承原始对象(字段都能复用的时候才考虑继承)
/**
* 是否勾选
*/
private Integer isCheck;
/**
* 子菜单列表
*/
private List<MenuNode> children;
public void addChild(MenuNode child){
if(children == null){
children = new ArrayList<>();
}
children.add(child);
}
}
四、测试
package com.server.utils.tree;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author visy.wang
* @date 2024/6/27 21:55
*/
public class Test {
public static void main(String[] args) {
List<Menu> menuList = new ArrayList<>();
//顺序可以任意调整,不影响结果
menuList.add(new Menu(1L, null, "菜单A", 1, 1,1));
menuList.add(new Menu(4L, 2L, "菜单BA", 2, 1,4));
menuList.add(new Menu(3L, 1L, "菜单AA", 2, 1,3));
menuList.add(new Menu(5L, 3L, "菜单AAA", 3, 1,5));
menuList.add(new Menu(2L, null, "菜单B", 1, 1,2));
//勾选的菜单ID集合
Set<Long> checkedMenuIds = new HashSet<>();
checkedMenuIds.add(3L);
checkedMenuIds.add(5L);
MenuNode root = TreeUtil.list2tree(
menuList, //原始列表
null, //根节点ID,用于提取顶层节点
Menu::getId, //获取ID的方法,也可以指定别的字段
Menu::getFid, //获取父ID的方法,也可以指定别的字段,但是必须和上面的方法对应
menu -> { //将列表表中的原始对象转换成节点对象(一般来说比原始对象多了对子节点集合的持有,除此之外也可以按需要增减字段)
MenuNode node = new MenuNode();//创建一个节点
BeanUtils.copyProperties(menu, node);//复制原始对象的字段到节点对象
node.setIsCheck(checkedMenuIds.contains(menu.getId()) ? 1 : 0);//单独设置其他字段
return node;//返回节点对象
},
"children", //子节点集合字段名
MenuNode::new, //节点对象的构造方法,用于创建一个新的父节点对象
MenuNode::addChild //指定添加子节点的方法
);
System.out.println(JSON.toJSONString(root.getChildren()));
}
}
五、打印结果
[
{
"children": [
{
"children": [
{
"fid": 3,
"id": 5,
"isCheck": 1,
"level": 3,
"name": "菜单AAA",
"status": 1,
"weight": 5
}
],
"fid": 1,
"id": 3,
"isCheck": 1,
"level": 2,
"name": "菜单AA",
"status": 1,
"weight": 3
}
],
"id": 1,
"isCheck": 0,
"level": 1,
"name": "菜单A",
"status": 1,
"weight": 1
},
{
"children": [
{
"fid": 2,
"id": 4,
"isCheck": 0,
"level": 2,
"name": "菜单BA",
"status": 1,
"weight": 4
}
],
"id": 2,
"isCheck": 0,
"level": 1,
"name": "菜单B",
"status": 1,
"weight": 2
}
]