不废话,直接上代码
一、工具函数
可以直接使用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}
]